Current best version

Figure 1 - This figures shows…

Figure 1 - This figures shows…

Code

  1. R environment setup
  2. Setting time breaks
  3. Defining origins
  4. Map for final figure
  5. Import raster data
  6. GAM vs. Loess smoothing models
  7. Compare rates between origins and not-origins
  8. Compare rates between different time periods
  9. Setup final figure
  10. Trend through time panel for final figure
  11. Assemble and print final figure

R environment setup

Attach libraries

library(png)
library(maptools)
Checking rgeos availability: TRUE
library(raster)
library(gam)
Loading required package: splines
Loading required package: foreach
foreach: simple, scalable parallel programming from Revolution Analytics
Use Revolution R for scalability, fault tolerance and more.
http://www.revolutionanalytics.com
Loaded gam 1.14
library(mapproj)
library(rgl)

Set working directory

setwd("~/Desktop/Botero postdoc 2016/Human density and the origins of agriculture/")

Setting time breaks

Define the times of agricultural origins

par(mar=c(0,0,0,20))
d <- readPNG("Larson_dates.png")
plot(seq(0,18, length.out = 19), seq(0,36, length.out = 19), type="n",ylim=c(0,36),xlim=c(0, 18), xaxt="n")
rasterImage(d, 0,0,18,36, interpolate=TRUE, col=d)
Start_of_early_window <- 16-12
End_of_early_window_start_of_late_window <- 8.2
End_of_late_window <- 17-4.2
polygon(x=c(Start_of_early_window, Start_of_early_window, End_of_early_window_start_of_late_window, End_of_early_window_start_of_late_window), y=c(0, 34, 34, 0), col=adjustcolor("limegreen", alpha= 0.2), border=adjustcolor("limegreen", alpha= 0.9))
polygon(x=c( End_of_early_window_start_of_late_window, End_of_early_window_start_of_late_window, End_of_late_window, End_of_late_window), y=c(0, 34, 34, 0), col=adjustcolor("firebrick", alpha= 0.2), border=adjustcolor("firebrick", alpha= 0.9))

These dates are provided in the supplimentary information for the Larson (2014) paper. I’ve copied those values into a .csv table provided here.

domestication_times <- read.csv("Domestication timing larson 2014.csv")
dim(domestication_times)
[1] 77  8
Region Species Start.Exploitation Finish.Exploitation Start.predomestication Finish.predomestication Start.Domestication Finish.Domestication
Southwest asia Wheat 12.00 11.25 11.25 11.00 11.00 9.00
Southwest asia Barley 12.00 11.25 11.25 10.50 10.50 9.00
Southwest asia Lentil 12.00 11.00 11.00 10.50 10.50 9.00
Southwest asia Pea 11.50 11.00 11.00 10.00 10.00 8.50
Southwest asia Chickpea 11.00 10.50 10.50 10.25 10.25 8.25
Southwest asia Broadbean NA NA NA NA 10.50 NA
Southwest asia Flax 12.00 9.50 NA NA 9.50 NA
Southwest asia Olive 10.00 6.00 NA NA 6.00 NA
Southwest asia Sheep 12.00 10.50 10.50 9.75 9.75 8.00
Southwest asia Goat 12.00 10.50 10.50 9.75 9.75 8.00
Southwest asia Pig 12.00 11.50 11.50 9.75 10.25 9.00
Southwest asia Cattle, taurine 11.50 10.50 10.50 10.25 10.25 8.00
Southwest asia Cat NA NA 10.50 4.00 4.00 NA
South Asia Tree cotton 8.50 4.50 NA NA 4.50 NA
South Asia Rice 8.00 5.00 5.00 4.00 4.00 2.50
South Asia Little millet NA NA NA NA 4.50 NA
South Asia Browntop millet NA NA NA NA 4.00 NA
South Asia Mungbean NA NA 4.50 3.50 3.50 3.00
South Asia Pigeonpea NA NA NA NA 3.50 NA
South Asia Zebu cattle 9.00 8.00 NA NA 8.00 6.50
South Asia Water buffalo 6.00 4.50 NA NA 4.50 NA
East Asia Broomcorn Millet 10.00 8.00 NA NA 8.00 NA
East Asia Foxtail millet 11.50 7.50 NA NA 7.50 NA
East Asia Rice 10.00 8.00 8.00 7.50 7.50 5.00
East Asia Soybean 8.50 5.50 NA NA 5.50 4.00
East Asia Ramie NA NA NA NA 5.25 NA
East Asia Melon 7.00 4.00 NA NA 4.00 3.75
East Asia Pig 12.00 8.50 NA NA 8.50 6.00
East Asia Silkworm 7.00 5.25 NA NA 5.25 NA
East Asia Yak NA NA NA NA 4.25 NA
East Asia Horse 7.50 6.75 6.75 5.50 5.50 4.00
East Asia Bactrian Camel NA NA NA NA 4.50 NA
East Asia Duck 2.50 1.00 NA NA 1.00 NA
East Asia Chicken 6.00 4.00 NA NA 4.00 NA
New Guinea Banana 10.00 7.00 7.00 4.00 4.00 NA
New Guinea Taro 10.00 7.00 7.00 4.00 NA NA
New Guinea Yam 10.00 7.00 7.00 4.00 NA NA
Africa and Arabia Date palm 7.00 6.00 NA NA 5.00 NA
Africa and Arabia Sorghum 8.00 4.00 NA NA 4.00 NA
Africa and Arabia Pearl millet NA NA NA NA 4.50 3.50
Africa and Arabia Fonio NA NA NA NA 2.50 NA
Africa and Arabia Cowpea NA NA NA NA 3.75 NA
Africa and Arabia Hyacinth bean NA NA NA NA 3.75 NA
Africa and Arabia Rice 3.50 2.00 NA NA 2.00 NA
Africa and Arabia Oil palm 9.25 3.50 NA NA 3.50 NA
Africa and Arabia Cattle NA NA 9.00 7.75 7.75 6.50
Africa and Arabia Donkey 9.00 5.50 NA NA 5.50 3.50
Africa and Arabia Dromedary camel 6.50 3.00 NA NA 3.00 NA
Africa and Arabia Guinea fowl NA NA 2.50 1.50 1.50 NA
North America Squash 6.50 5.00 NA NA 5.00 NA
North America Sunflower 6.00 4.75 NA NA 4.00 NA
North America Sumpweed 6.00 4.50 NA NA 4.00 NA
North America Pitseed goosefoot 4.75 3.75 NA NA 3.75 NA
Meso-america Squash (pepo) NA NA NA NA 10.00 9.50
Meso-america Maize 10.00 9.00 NA NA 9.00 NA
Meso-america Foxtail millet-grass NA NA NA NA 6.00 4.00
Meso-america Common bean NA NA NA NA 3.00 NA
Meso-america Avocado NA NA NA NA 3.00 NA
Meso-america Chile pepper NA NA NA NA 3.00 NA
Meso-america Turkey NA NA NA NA 2.00 NA
South America Chili pepper NA NA NA NA 6.00 NA
South America Peanut NA NA NA NA 5.00 NA
South America Cotton NA NA NA NA 6.00 NA
South America Coca NA NA NA NA 8.00 NA
South America Now-minor root crops (arrowroot, leren) NA NA NA NA 9.00 NA
South America Squash NA NA NA NA 10.00 NA
South America Common bean NA NA NA NA 5.00 NA
South America Lima bean NA NA 8.25 NA 6.00 NA
South America Monioc NA NA NA NA 7.00 NA
South America Sweet potato NA NA NA NA 5.00 NA
South America White potato 7.00 4.50 NA NA 4.00 NA
South America Quinoa 5.00 NA NA NA 3.50 NA
South America Yam NA NA NA NA 5.50 NA
South America Llama 10.00 6.00 NA NA 6.00 NA
South America Alpaca 10.00 5.00 NA NA 5.00 NA
South America Guinea pig NA NA NA NA 5.00 NA
South America Muscovy Duck NA NA NA NA 4.00 NA
par(mar=c(5,4,6,1))
dates <- unlist(domestication_times[3:8])
hist(dates, breaks = 22, xlim=c(15,0), xlab="K years ago", col=adjustcolor("cornflowerblue", alpha= 0.5), border=adjustcolor("cornflowerblue", alpha= 0.9), main="All dates in dataset"  )
mtext("This tells us about how evenly our evidence is distributed in time", 3, line=1)

hist(dates, breaks = 22, xlim=c(15,0), xlab="Thousand years ago", col=adjustcolor("cornflowerblue", alpha= 0.5), border=adjustcolor("cornflowerblue", alpha= 0.9), main="All dates in dataset with Larson(2014) date windows")
Start_of_early_window <- 12
End_of_early_window_start_of_late_window <- 8.2
End_of_late_window <- 4.2
polygon(x=c(Start_of_early_window, Start_of_early_window, End_of_early_window_start_of_late_window, End_of_early_window_start_of_late_window), y=c(0, 30, 30, 0), col=adjustcolor("limegreen", alpha= 0.2), border=adjustcolor("limegreen", alpha= 0.9))
polygon(x=c( End_of_early_window_start_of_late_window, End_of_early_window_start_of_late_window, End_of_late_window, End_of_late_window), y=c(0, 30, 30, 0), col=adjustcolor("firebrick", alpha= 0.2), border=adjustcolor("firebrick", alpha= 0.9))
hist(dates, breaks = 22, xlim=c(15,0), xlab="K years ago", col=adjustcolor("cornflowerblue", alpha= 0.2), border=adjustcolor("cornflowerblue", alpha= 0.9), main="", add=TRUE)
mtext("Early Holocene", 3, line = -1, adj=.3)
mtext("Middle Holocene", 3, line= -1, adj=.6)

par(mfrow=c(2,3), mar=c(4,4,2,0))
dim(domestication_times)
[1] 77  8
specific_dates <- domestication_times[,3:8]
for(i in c(1, 3, 5, 2, 4, 6)){
hist(specific_dates[,i], breaks = 22, xlim=c(15,0), xlab="Thousand years ago", col=adjustcolor("cornflowerblue", alpha= 0.5), border=adjustcolor("cornflowerblue", alpha= 0.9), main= names(specific_dates)[i])
Start_of_early_window <- 12
End_of_early_window_start_of_late_window <- 8.2
End_of_late_window <- 4.2
polygon(x=c(Start_of_early_window, Start_of_early_window, End_of_early_window_start_of_late_window, End_of_early_window_start_of_late_window), y=c(0, 30, 30, 0), col=adjustcolor("limegreen", alpha= 0.2), border=adjustcolor("limegreen", alpha= 0.9))
polygon(x=c( End_of_early_window_start_of_late_window, End_of_early_window_start_of_late_window, End_of_late_window, End_of_late_window), y=c(0, 30, 30, 0), col=adjustcolor("firebrick", alpha= 0.2), border=adjustcolor("firebrick", alpha= 0.9))
hist(specific_dates[,i], breaks = 22, xlim=c(15,0), xlab="K years ago", col=adjustcolor("cornflowerblue", alpha= 0.2), border=adjustcolor("cornflowerblue", alpha= 0.9), main="", add=TRUE)
}

I’m creating new rows for this table, combining dates in different ways to make the CDFs below look more authentic. This makes it so that pre-ag always happens before post-ag. What I’ve done is given the later date to the earlier date when those dates are missing.

h <- which(is.na(domestication_times[,3]))
domestication_times <- cbind(domestication_times, rep(NA, length(domestication_times[,1])))
domestication_times[,9] <- domestication_times[,3]
domestication_times[h,9] <- domestication_times[h,7]
colnames(domestication_times)[9] <- "adopt exploitation date"
domestication_times[,10] <- domestication_times[,7]
domestication_times[which(is.na(domestication_times[,10])),10] <- 0
colnames(domestication_times)[10] <- "start of ag"
#save(domestication_times, file="~/Desktop/Human density and the origins of agriculture/Domestication timing larson 2014.Rdata")

I think these are best described by a cummulative distribution, showing how they accumulate over time.

for(i in 1:8){
type_number <- i
    match <- domestication_times[ which(domestication_times$Region == levels(domestication_times$Region)[ type_number]), 9]
    maxer <- max(match, na.rm=TRUE)
    j <- ecdf(maxer-match)
    print(levels(domestication_times$Region)[ type_number])
    print(match)
    print(j)
}
[1] "Africa and Arabia"
 [1] 7.00 8.00 4.50 2.50 3.75 3.75 3.50 9.25 7.75 9.00 6.50 1.50
Empirical CDF 
Call: ecdf(maxer - match)
 x[1:11] =      0,   0.25,   1.25,  ...,   6.75,   7.75
[1] "East Asia"
 [1] 10.00 11.50 10.00  8.50  5.25  7.00 12.00  7.00  4.25  7.50  4.50  2.50  6.00
Empirical CDF 
Call: ecdf(maxer - match)
 x[1:11] =      0,    0.5,      2,  ...,   7.75,    9.5
[1] "Meso-america"
[1] 10 10  6  3  3  3  2
Empirical CDF 
Call: ecdf(maxer - match)
 x[1:4] =      0,      4,      7,      8
[1] "New Guinea"
[1] 10 10 10
Empirical CDF 
Call: ecdf(maxer - match)
 x[1:1] =      0
[1] "North America"
[1] 6.50 6.00 6.00 4.75
Empirical CDF 
Call: ecdf(maxer - match)
 x[1:3] =      0,    0.5,   1.75
[1] "South America"
 [1]  6.0  5.0  6.0  8.0  9.0 10.0  5.0  6.0  7.0  5.0  7.0  5.0  5.5 10.0 10.0  5.0  4.0
Empirical CDF 
Call: ecdf(maxer - match)
 x[1:8] =      0,      1,      2,  ...,      5,      6
[1] "South Asia"
[1] 8.5 8.0 4.5 4.0 3.5 3.5 9.0 6.0
Empirical CDF 
Call: ecdf(maxer - match)
 x[1:7] =      0,    0.5,      1,  ...,      5,    5.5
[1] "Southwest asia"
 [1] 12.0 12.0 12.0 11.5 11.0 10.5 12.0 10.0 12.0 12.0 12.0 11.5  4.0
Empirical CDF 
Call: ecdf(maxer - match)
 x[1:6] =      0,    0.5,      1,  ...,      2,      8
par(mfcol=c(2,5), mar=c(4,0,5,0))
plot(0,0, type="n", xaxt="n", xlab="", bty="n")
mtext("Percent of species that will eventually \n be domesticated in a region", 2, line=-5, cex=0.5)
plot(0,0, type="n", xaxt="n", xlab="", bty="n")
mtext("Percent of species that will eventually \n be domesticated in a region", 2, line=-5, cex=0.5)
for(i in 1:8){
type_number <- i
    match <- domestication_times[ which(domestication_times$Region == levels(domestication_times$Region)[ type_number]), 9]
    maxer <- max(match, na.rm=TRUE)
    j <- ecdf(maxer-match)
    #print(j)
    
plot(0,0, xlim=c(15,0), ylim=c(0,100), ylab="Percent of species that will eventually \n be domesticated in a region", xlab="Thousand years ago", main=levels(domestication_times$Region)[ type_number], type="n", yaxt="n")
x_seq <- rev(c(0,seq(0, maxer, length.out=100)))
y_seq <- 100 * (c(0, j(seq(0, maxer, length.out=100))))
lines(x_seq, y_seq,  ylim=c(-1,1))
polygon(c(0, x_seq), c(0, y_seq), border=adjustcolor("cornflowerblue",alpha=1), col=adjustcolor("cornflowerblue", alpha=0.2))
if(i == 2 | i == 1)axis(2)
if(i == 3)mtext("Cummulative distribution function for the accumulation of domesticates", 3, line=3.8, col="cornflowerblue")
}

par(mfcol=c(2,5), mar=c(4,0,5,0))
plot(0,0, type="n", xaxt="n", xlab="", bty="n")
mtext("Percent of species that will eventually \n be domesticated in a region", 2, line=-5, cex=0.5)
plot(0,0, type="n", xaxt="n", xlab="", bty="n")
mtext("Percent of species that will eventually \n be domesticated in a region", 2, line=-5, cex=0.5)
for(i in 1:8){
type_number <- i
    match <- domestication_times[ which(domestication_times$Region == levels(domestication_times$Region)[ type_number]), 9]
    maxer <- max(match, na.rm=TRUE)
    j <- ecdf(maxer-match)
    #print(j)
    
plot(0,0, xlim=c(15,0), ylim=c(0,100), ylab="Percent of species that will eventually \n be domesticated in a region", xlab="Thousand years ago", main=levels(domestication_times$Region)[ type_number], type="n", yaxt="n")
x_seq <- rev(c(0,seq(0, maxer, length.out=100)))
y_seq <- 100 * (c(0, j(seq(0, maxer, length.out=100))))
lines(x_seq, y_seq,  ylim=c(-1,1))
polygon(c(0, x_seq), c(0, y_seq), border=adjustcolor("cornflowerblue",alpha=1), col=adjustcolor("cornflowerblue", alpha=0.2))
abline(v= maxer - quantile(j)[2], col="limegreen", lwd=2)
if(i == 2 | i == 1)axis(2)
if(i == 2)mtext("25%", 3, line=3.5, adj=-1, col="limegreen")
if(i == 3)mtext("Cummulative distribution function for the accumulation of domesticates", 3, line=3.8, col="cornflowerblue")
if(i == 4)mtext("Choose a y to predict an x", 3, line=3.3, col="cornflowerblue")
    break_one <- maxer
            break_two <- maxer - quantile(j)[2]
                
    polygon(x=c(break_two, break_two, break_one, break_one), y=c(0, 1, 1, 0), col=adjustcolor("cornflowerblue", alpha=0.2), border=adjustcolor("cornflowerblue",alpha=1))
            lines(x=c(break_two, break_two), y=c(0,-1), col="cornflowerblue")
            abline(h = 25, col="limegreen", lwd=2)
}

Make this a function. There is a choice of two methods here. At the end of this section we need to print the desision we’re passing to the later analyses.

Defining origins

origins <- readShapePoly('Origins_updated.shp')
proj4string(origins) <- CRS("+proj=longlat +datum=WGS84")
NOTE: rgdal::checkCRSArgs: no proj_defs.dat in PROJ.4 shared files
#proj <- CRS("+proj=eqc +lat_ts=0 +lat_0=0 +lon_0=0 +x_0=0 +y_0=0 +ellps=WGS84 +units=m +no_defs")
#origins.ea <- spTransform(origins, proj)
#subset_order <- c(1, 2, 3, 5, 6, 8, 9, 10, 11, 12, 17, 18)
subset_order <- c(8, 10, 9, 5, 18, 7, 6, 20, 1, 2, 13, 14)
origins_subset <- origins[subset_order,]
origins_subset$CONTINENT
 [1] Mesoamerica   NW_Lowland_SA N_Lowland_SA  Fertile_Cresc Chinese_loess New_Guinea   
 [7] E_North_Ameri C/S_Andes     W_African_Sav Sudanic_Savan Ganges_E_Indi Lower-MiddleY
20 Levels: C/S_Andes Chinese_loess E_North_Ameri Ethipian plat Fertile_Cresc ... West Africa T
origins_subset$name
NULL
map()
map(origins, add=TRUE, fill=TRUE, col=adjustcolor("cornflowerblue", alpha=1))
database does not (uniquely) contain the field 'name'.

map for final figure

Make the map for the center panel (#5 on layout panel)

d <- readPNG("earth.png")
png(file=paste("40961.png",sep=""),width=1440,height=720, bg="transparent")
par(mar=c(0,0,0,0), mai=c(0,0,0,0))
plot(seq(-180, 180, length.out = 19), seq(-90, 90, length.out = 19), type="n",xlim=c(-180, 180),ylim=c(-90, 90), xaxt="n")
x_adj <- 0
y_adj <- 0
rasterImage(d, -180 - x_adj, -90 - y_adj, 180 + x_adj, 90 + y_adj, interpolate=TRUE, col=d)
#polygon(x=c(-180,-180, 180,180), y=c(-90, 90, 90, -90), col=adjustcolor("white", alpha=0.1))
#rasterImage(d, -13.5, -13.5, 375, 375, interpolate=TRUE, col=d)
plot(origins_subset, add=TRUE, col=adjustcolor("white", alpha=.8), xaxt="n", border="white", lwd=4) #still need to reproject!!!
LW <- 4
lines(x = c(-100 , -130), y = c(20 , 90), lty=2, col=adjustcolor("white", alpha=1), lwd=LW) #Mesoamerica
lines(x = c(-80 , -70), y = c(0 , 90), lty=2, col=adjustcolor("white", alpha=1), lwd=LW) #NW_Lowland_SA
lines(x = c(-74 , -5 ), y = c(5 , 90), lty=2, col=adjustcolor("white", alpha=1), lwd=LW) #N_Lowland_SA
lines(x = c(40 , 30), y = c(36 , 90), lty=2, col=adjustcolor("white", alpha=1), lwd=LW) #Fertile_Cresc
lines(x = c(110 , 70), y = c(40 , 90), lty=2, col=adjustcolor("white", alpha=1), lwd=LW) #Chinese_loess
lines(x = c(142 , 160), y = c(-5 , 90), lty=2, col=adjustcolor("white", alpha=1), lwd=LW) #New_Guinea
lines(x = c(-85 , -130), y = c(33 , -90), lty=2, col=adjustcolor("white", alpha=1), lwd=LW) #E_North_Ameri
lines(x = c(-68 , -75), y = c(-25 , -90), lty=2, col=adjustcolor("white", alpha=1), lwd=LW) #C/S_Andes
lines(x = c(-10 , -20), y = c( 15, -90), lty=2, col=adjustcolor("white", alpha=1), lwd=LW) #W_African_Sav
lines(x = c(25 , 40), y = c(9 , -90), lty=2, col=adjustcolor("white", alpha=1), lwd=LW) #Sudanic_Savan
lines(x = c(87 , 100), y = c(20 , -90), lty=2, col=adjustcolor("white", alpha=1), lwd=LW) #Ganges_E_Indi
lines(x = c(120 , 160), y = c(30 , -90), lty=2, col=adjustcolor("white", alpha=1), lwd=LW) #Lower-MiddleY
dev.off()
null device 
          1 
#need to overplot so it fills the whole frame. the default plot leaves a border around the photo
d <- readPNG("40961.png")
png(file=paste("40962.png",sep=""),width=1440,height=720, bg="transparent")
par(mar=c(0,0,0,0), mai=c(0,0,0,0))
plot(seq(-180, 180, length.out = 19), seq(-90, 90, length.out = 19), type="n",xlim=c(-180, 180),ylim=c(-90, 90), xaxt="n")
x_adj <- 30
y_adj <- 15
rasterImage(d, -180 - x_adj, -90 - y_adj, 180 + x_adj, 90 + y_adj, interpolate=TRUE, col=d)
dev.off()
null device 
          1 

Import raster data

unit = potential human density and projected productivity

#subset and reorder origins. This is currently done at the end of the plot but should be moved forward.
# Load data for population density
load("PopD_all_December.rdata")
PopD.ALL
class       : RasterStack 
dimensions  : 288, 720, 207360, 18  (nrow, ncol, ncell, nlayers)
resolution  : 0.5, 0.5  (x, y)
extent      : -180, 180, -60, 84  (xmin, xmax, ymin, ymax)
coord. ref. : +proj=longlat +datum=WGS84 +no_defs +ellps=WGS84 +towgs84=0,0,0 
names       :        fourK,        fiveK,         sixK,       sevenK,       eightK,        nineK,         tenK,      elevenK,      twelveK,    thirteenK,    fourteenK,     fifteenK,     sixteenK,   seventeenK,    eighteenK, ... 
min values  : 5.611358e-07, 1.067142e-06, 2.508241e-06, 6.317553e-06, 2.286934e-05, 7.631922e-05, 1.272693e-04, 2.118215e-04, 2.602175e-04, 3.226203e-04, 4.390267e-04, 5.572032e-04, 7.313966e-04, 8.286005e-04, 8.297062e-04, ... 
max values  :     2.051069,     2.013452,     2.142908,     1.888403,     1.863014,     1.880628,     1.650615,     1.678033,     1.697732,     1.499115,     1.517264,     1.443677,     1.464867,     1.453581,     1.436394, ... 
# Extract data to a matrix
Pop <- values(PopD.ALL)
r <- raster(PopD.ALL, 1)
r
class       : RasterLayer 
dimensions  : 288, 720, 207360  (nrow, ncol, ncell)
resolution  : 0.5, 0.5  (x, y)
extent      : -180, 180, -60, 84  (xmin, xmax, ymin, ymax)
coord. ref. : +proj=longlat +datum=WGS84 +no_defs +ellps=WGS84 +towgs84=0,0,0 
data source : in memory
names       : fourK 
values      : 5.611358e-07, 2.051069  (min, max)

GAM vs Loess smoothing models

Justification for smoothing Models.

We need to justify our decision to use a GAM over other models. This should include citations to back up those arguments.

This is a data fitting model, not a theory driven model. There is no process or mechanism built into the model, the model is just flexably fitting the data. There are many ways of doing this, but here we’re competing the GAM and Loess against eachother to tell us a better story. The models are very similar, but slightly different. The Loess model is a local averaging model where the flexible parameter is how many of the surrounding points to take into consideration, and the GAM is a global averaging model where the flexible parameter is the number of bends in the line and then those bends are fit in the best way possible. The advantage of GAM is that it produces a translatable model that could be fit to different datasets. The disadvantage of GAM is that it often produces poor data fits. The Loess model moves along the x-axes and averages along the way. The advantage of a loess is that it tends to track the data nicely with few parameters, but it doesn’t produce a transportable model for comparing against other datasets.

about LOESS from the internet: http://www.itl.nist.gov/div898/handbook/pmd/section1/pmd144.htm

“LOESS, originally proposed by Cleveland (1979) and further developed by Cleveland and Devlin (1988), specifically denotes a method that is (somewhat) more descriptively known as locally weighted polynomial regression. At each point in the data set a low-degree polynomial is fit to a subset of the data, with explanatory variable values near the point whose response is being estimated. The polynomial is fit using weighted least squares, giving more weight to points near the point whose response is being estimated and less weight to points further away. The value of the regression function for the point is then obtained by evaluating the local polynomial using the explanatory variable values for that data point. The LOESS fit is complete after regression function values have been computed for each of the n data points. Many of the details of this method, such as the degree of the polynomial model and the weights, are flexible. The local polynomials fit to each subset of the data are almost always of first or second degree; that is, either locally linear (in the straight line sense) or locally quadratic. Using a zero degree polynomial turns LOESS into a weighted moving average. Such a simple local model might work well for some situations, but may not always approximate the underlying function well enough. Higher-degree polynomials would work in theory, but yield models that are not really in the spirit of LOESS. LOESS is based on the ideas that any function can be well approximated in a small neighborhood by a low-order polynomial and that simple models can be fit to data easily. High-degree polynomials would tend to overfit the data in each subset and are numerically unstable, making accurate computations difficult. The biggest advantage LOESS has over many other methods is the fact that it does not require the specification of a function to fit a model to all of the data in the sample. Instead the analyst only has to provide a smoothing parameter value and the degree of the local polynomial. In addition, LOESS is very flexible, making it ideal for modeling complex processes for which no theoretical models exist. These two advantages, combined with the simplicity of the method, make LOESS one of the most attractive of the modern regression methods for applications that fit the general framework of least squares regression but which have a complex deterministic structure. Although LOESS does share many of the best features of other least squares methods, efficient use of data is one advantage that LOESS doesn’t share. LOESS requires fairly large, densely sampled data sets in order to produce good models. This is not really surprising, however, since LOESS needs good empirical information on the local structure of the process in order perform the local fitting. In fact, given the results it provides, LOESS could arguably be more efficient overall than other methods like nonlinear least squares. It may simply frontload the costs of an experiment in data collection but then reduce analysis costs. Another disadvantage of LOESS is the fact that it does not produce a regression function that is easily represented by a mathematical formula. This can make it difficult to transfer the results of an analysis to other people. In order to transfer the regression function to another person, they would need the data set and software for LOESS calculations. In nonlinear regression, on the other hand, it is only necessary to write down a functional form in order to provide estimates of the unknown parameters and the estimated uncertainty. Depending on the application, this could be either a major or a minor drawback to using LOESS. Finally, as discussed above, LOESS is a computational intensive method. This is not usually a problem in our current computing environment, however, unless the data sets being used are very large. LOESS is also prone to the effects of outliers in the data set, like other least squares methods. There is an iterative, robust version of LOESS [Cleveland (1979)] that can be used to reduce LOESS’ sensitivity to outliers, but extreme outliers can still overcome even the robust method. \(q\) is called the smoothing parameter because it controls the flexibility of the LOESS regression function. Large values of q produce the smoothest functions that wiggle the least in response to fluctuations in the data. The smaller \(q\) is, the closer the regression function will conform to the data. Using too small a value of the smoothing parameter is not desirable, however, since the regression function will eventually start to capture the random error in the data. Useful values of the smoothing parameter typically lie in the range \(0.25\) to \(0.5\) for most LOESS applications.”

about GAM from the internet: http://multithreaded.stitchfix.com/blog/2015/07/30/gam/

“Imagine that you step into a room of data scientists; the dress code is casual and the scent of strong coffee is hanging in the air. You ask the data scientists if they regularly use generalized additive models (GAM) to do their work. Very few will say yes, if any at all.

Now let’s replay the scenario, only this time we replace GAM with, say, random forest or support vector machines (SVM). Everyone will say yes, and you might even spark a passionate debate.

Despite its lack of popularity in the data science community, GAM is a powerful and yet simple technique. Hence, the purpose of this post is to convince more data scientists to use GAM. Of course, GAM is no silver bullet, but it is a technique you should add to your arsenal. Here are three key reasons:

Easy to interpret.

Flexible predictor functions can uncover hidden patterns in the data.

Regularization of predictor functions helps avoid overfitting.

In general, GAM has the interpretability advantages of GLMs where the contribution of each independent variable to the prediction is clearly encoded. However, it has substantially more flexibility because the relationships between independent and dependent variable are not assumed to be linear. In fact, we don’t have to know a priori what type of predictive functions we will eventually need. From an estimation standpoint, the use of regularized, nonparametric functions avoids the pitfalls of dealing with higher order polynomial terms in linear models. From an accuracy standpoint, GAMs are competitive with popular learning techniques.

Generalized additive models were originally invented by Trevor Hastie and Robert Tibshirani in 1986 (see [1], [2]). The GAM framework is based on an appealing and simple mental model:

Relationships between the individual predictors and the dependent variable follow smooth patterns that can be linear or nonlinear.

We can estimate these smooth relationships simultaneously and then predict $$g(E(Y)))$$

by simply adding them up.

Mathematically speaking, GAM is an additive modeling technique where the impact of the predictive variables is captured through smooth functions which—depending on the underlying patterns in the data—can be nonlinear.

References

[1] Hastie, Trevor and Tibshirani, Robert. (1990), Generalized Additive Models, New York: Chapman and Hall.

[2] Hastie, Trevor and Tibshirani, Robert. (1986), Generalized Additive Models, Statistical Science, Vol. 1, No 3, 297-318.

[3] Wood, S. N. (2006), Generalized Additive Models: an introduction with R, Boca Raton: Chapman & Hall/CRC

[4] Wood, S. N. (2004). Stable and efficient multiple smoothing parameter estimation for generalized additive models. Journal of the American Statistical Association 99, 673–686

[5] Marx, Brian D and Eilers, Paul H.C. (1998). Direct generalized additive modeling with penalized likelihood, Computational Statistics & Data Analysis 28 (1998) 193-20

[6] Sinha, Samiran, A very short note on B-splines, http://www.stat.tamu.edu/~sinha/research/note1.PDF

[7] German Rodrıguez (2001), Smoothing and Non-Parametric Regression, http://data.princeton.edu/eco572/smoothing.pd

[8] Notes on GAM By Simon Wood. http://people.bath.ac.uk/sw283/mgcv/tampere/gam.PDF

[9] Notes on Smoothing Parameter Selection By Simon Wood, http://people.bath.ac.uk/sw283/mgcv/tampere/smoothness.PDF

[10] Notes on REML & GAM By Simon Wood, http://people.bath.ac.uk/sw283/talks/REML.PDF

[11] Karatzoglou, Alexandros, Meyer, David and Hornik, Kurt (2006), Support Vector Machines in R, Journal of Statistical Software Volume 15, Issue 9, http://www.jstatsoft.org/v15/i09/paper

[12] “e1071” package, https://cran.r-project.org/web/packages/e1071/e1071.PDF

[13] “mgcv” package, https://cran.r-project.org/web/packages/mgcv/mgcv.PDF

[14] “gam” package, https://cran.r-project.org/web/packages/gam/gam.PDF

[15] “randomForestSRC” package, https://cran.r-project.org/web/packages/randomForestSRC/randomForestSRC.PDF

or from: http://plantecology.syr.edu/fridley/bio793/gam.html

GAMs in R are a nonparametric extension of GLMs, used often for the case when you have no a priori reason for choosing a particular response function (such as linear, quadratic, etc.) and want the data to ‘speak for themselves’. GAMs do this via a smoothing function, similar to what you may already know about locally weighted regressions. GAMs take each predictor variable in the model and separate it into sections (delimited by ‘knots’), and then fit polynomial functions to each section separately, with the constraint that there are no kinks at the knots (second derivatives of the separate functions are equal at the knots). The number of parameters used for such fitting is obviously more than what would be necessary for a simpler parametric fit to the same data, but computational shortcuts mean the model degrees of freedom is usually lower than what you might expect from a line with so much ‘wiggliness’. Indeed this is the principal statistical issue associated with GAM modeling: minimizing residual deviance (goodness of fit) while maximizing parsimony (lowest possible degrees of freedom). Since the model fit is based on deviance/likelihood, fitted models are directly comparable with GLMs using likelihood techniques (like AIC) or classical tests based on model deviance (Chi-squared or F tests, depending on the error structure). Even better, all the error and link structures of GLMs are available in GAMs (including poisson and binomial), as are the standard suite of lm or glm attributes (resid, fitted, summary, coef, etc.). A principal reason why GAMs are often less preferred than GLMs is that the results are often difficult to interpret because no parameter values are returned (although significance tests of each term are). They can be very good for prediction/interpolation, as well as exploratory analyses about the functional nature of a response. Some researchers examine the shape of a curve with GAMs, then reconstruct the curve shape parametrically with GLMs for model building. There are two common implementations of GAMs in R. The older version (originally made for S-PLUS) is available as the ‘gam’ package by Hastie and Tibshirani. The newer version that we will use below is the ‘mgcv’ package from Simon Wood. The basic modeling procedure for both packages is similar (the function is gam for both; be wary of having both libraries loaded at the same time), but the behind-the-scenes computational approaches differ, as do the arguments for optimization and the model output. Expect the results to be slightly different when used with the same model structure on the same dataset."

These are the most uninformed models you can use. We can certainly try to add some process to these models and try to get some more reliable numbers, but this will involve more work.

Fit and plot GAM model with different degrees of freedom

We should make our decisions very transparent here. We should be able to justify our decision of 3 degrees of freedom over other possible values.

Density projections

# Read the polygons
library(rgdal)
rgdal: version: 1.2-5, (SVN revision 648)
 Geospatial Data Abstraction Library extensions to R successfully loaded
 Loaded GDAL runtime: GDAL 2.1.2, released 2016/10/24
 Path to GDAL shared files: 
 Loaded PROJ.4 runtime: Rel. 4.9.1, 04 March 2015, [PJ_VERSION: 491]
 Path to PROJ.4 shared files: (autodetected)
WARNING: no proj_defs.dat in PROJ.4 shared files
 Linking to sp version: 1.2-3 
getwd()
[1] "/Users/Ty/Desktop/Botero postdoc 2016/Human density and the origins of agriculture"
origins <- readShapePoly('Origins_updated.shp')
proj4string(origins) 
[1] NA
# Extract data
library(raster)
e <- extent(-180, 180, -60, 84)
all_cells <- extract(r, e, cellnumber = TRUE)
NOTE: rgdal::checkCRSArgs: no proj_defs.dat in PROJ.4 shared files
NOTE: rgdal::checkCRSArgs: no proj_defs.dat in PROJ.4 shared files
per.origin <- extract(r, origins, cellnumber = TRUE, buffer = 100000)
NOTE: rgdal::checkCRSArgs: no proj_defs.dat in PROJ.4 shared files
NOTE: rgdal::checkCRSArgs: no proj_defs.dat in PROJ.4 shared files
NOTE: rgdal::checkCRSArgs: no proj_defs.dat in PROJ.4 shared files
NOTE: rgdal::checkCRSArgs: no proj_defs.dat in PROJ.4 shared files
NOTE: rgdal::checkCRSArgs: no proj_defs.dat in PROJ.4 shared files
NOTE: rgdal::checkCRSArgs: no proj_defs.dat in PROJ.4 shared files
NOTE: rgdal::checkCRSArgs: no proj_defs.dat in PROJ.4 shared files
NOTE: rgdal::checkCRSArgs: no proj_defs.dat in PROJ.4 shared files
NOTE: rgdal::checkCRSArgs: no proj_defs.dat in PROJ.4 shared files
NOTE: rgdal::checkCRSArgs: no proj_defs.dat in PROJ.4 shared files
NOTE: rgdal::checkCRSArgs: no proj_defs.dat in PROJ.4 shared files
NOTE: rgdal::checkCRSArgs: no proj_defs.dat in PROJ.4 shared files
NOTE: rgdal::checkCRSArgs: no proj_defs.dat in PROJ.4 shared files
NOTE: rgdal::checkCRSArgs: no proj_defs.dat in PROJ.4 shared files
NOTE: rgdal::checkCRSArgs: no proj_defs.dat in PROJ.4 shared files
NOTE: rgdal::checkCRSArgs: no proj_defs.dat in PROJ.4 shared files
NOTE: rgdal::checkCRSArgs: no proj_defs.dat in PROJ.4 shared files
NOTE: rgdal::checkCRSArgs: no proj_defs.dat in PROJ.4 shared files
NOTE: rgdal::checkCRSArgs: no proj_defs.dat in PROJ.4 shared files
NOTE: rgdal::checkCRSArgs: no proj_defs.dat in PROJ.4 shared files
NOTE: rgdal::checkCRSArgs: no proj_defs.dat in PROJ.4 shared files
NOTE: rgdal::checkCRSArgs: no proj_defs.dat in PROJ.4 shared files
NOTE: rgdal::checkCRSArgs: no proj_defs.dat in PROJ.4 shared files
NOTE: rgdal::checkCRSArgs: no proj_defs.dat in PROJ.4 shared files
NOTE: rgdal::checkCRSArgs: no proj_defs.dat in PROJ.4 shared files
NOTE: rgdal::checkCRSArgs: no proj_defs.dat in PROJ.4 shared files
NOTE: rgdal::checkCRSArgs: no proj_defs.dat in PROJ.4 shared files
NOTE: rgdal::checkCRSArgs: no proj_defs.dat in PROJ.4 shared files
NOTE: rgdal::checkCRSArgs: no proj_defs.dat in PROJ.4 shared files
NOTE: rgdal::checkCRSArgs: no proj_defs.dat in PROJ.4 shared files
NOTE: rgdal::checkCRSArgs: no proj_defs.dat in PROJ.4 shared files
NOTE: rgdal::checkCRSArgs: no proj_defs.dat in PROJ.4 shared files
NOTE: rgdal::checkCRSArgs: no proj_defs.dat in PROJ.4 shared files
NOTE: rgdal::checkCRSArgs: no proj_defs.dat in PROJ.4 shared files
NOTE: rgdal::checkCRSArgs: no proj_defs.dat in PROJ.4 shared files
NOTE: rgdal::checkCRSArgs: no proj_defs.dat in PROJ.4 shared files
NOTE: rgdal::checkCRSArgs: no proj_defs.dat in PROJ.4 shared files
NOTE: rgdal::checkCRSArgs: no proj_defs.dat in PROJ.4 shared files
NOTE: rgdal::checkCRSArgs: no proj_defs.dat in PROJ.4 shared files
NOTE: rgdal::checkCRSArgs: no proj_defs.dat in PROJ.4 shared files
NOTE: rgdal::checkCRSArgs: no proj_defs.dat in PROJ.4 shared files
length(all_cells)
[1] 414720
for(i in 1:20){
all_cells <- all_cells[-which(per.origin[[i]][,1] %in% all_cells[,1]), ]
}
length(all_cells)
[1] 408656
names(per.origin) <- origins@data[, 1]
str(all_cells)
 num [1:204328, 1:2] 3033 3034 3035 3036 3037 ...
 - attr(*, "dimnames")=List of 2
  ..$ : NULL
  ..$ : chr [1:2] "cell" "value"
str(per.origin)
List of 20
 $ W_African_Sav: num [1:309, 1:2] 92506 92507 92508 92509 92510 ...
  ..- attr(*, "dimnames")=List of 2
  .. ..$ : NULL
  .. ..$ : chr [1:2] "cell" "value"
 $ Sudanic_Savan: num [1:306, 1:2] 99050 99051 99052 99053 99054 ...
  ..- attr(*, "dimnames")=List of 2
  .. ..$ : NULL
  .. ..$ : chr [1:2] "cell" "value"
 $ West Africa T: num [1:427, 1:2] 102609 102610 102611 103326 103327 ...
  ..- attr(*, "dimnames")=List of 2
  .. ..$ : NULL
  .. ..$ : chr [1:2] "cell" "value"
 $ Ethipian plat: num [1:275, 1:2] 106281 106282 106283 106998 106999 ...
  ..- attr(*, "dimnames")=List of 2
  .. ..$ : NULL
  .. ..$ : chr [1:2] "cell" "value"
 $ Fertile_Cresc: num [1:195, 1:2] 67404 67405 67406 68119 68120 ...
  ..- attr(*, "dimnames")=List of 2
  .. ..$ : NULL
  .. ..$ : chr [1:2] "cell" "value"
 $ E_North_Ameri: num [1:170, 1:2] 64270 64271 64984 64985 64986 ...
  ..- attr(*, "dimnames")=List of 2
  .. ..$ : NULL
  .. ..$ : chr [1:2] "cell" "value"
 $ New_Guinea   : num [1:24, 1:2] 127360 127361 127362 128080 128081 ...
  ..- attr(*, "dimnames")=List of 2
  .. ..$ : NULL
  .. ..$ : chr [1:2] "cell" "value"
 $ Mesoamerica  : num [1:73, 1:2] 93032 93033 93034 93035 93036 ...
  ..- attr(*, "dimnames")=List of 2
  .. ..$ : NULL
  .. ..$ : chr [1:2] "cell" "value"
 $ N_Lowland_SA : num [1:39, 1:2] 110373 110374 110375 111092 111093 ...
  ..- attr(*, "dimnames")=List of 2
  .. ..$ : NULL
  .. ..$ : chr [1:2] "cell" "value"
 $ NW_Lowland_SA: num [1:24, 1:2] 123319 123320 123321 123322 123323 ...
  ..- attr(*, "dimnames")=List of 2
  .. ..$ : NULL
  .. ..$ : chr [1:2] "cell" "value"
 $ Sava_W_India : num [1:34, 1:2] 83312 84031 84032 84749 84750 ...
  ..- attr(*, "dimnames")=List of 2
  .. ..$ : NULL
  .. ..$ : chr [1:2] "cell" "value"
 $ S_India      : num [1:18, 1:2] 99153 99154 99872 99873 99874 ...
  ..- attr(*, "dimnames")=List of 2
  .. ..$ : NULL
  .. ..$ : chr [1:2] "cell" "value"
 $ Ganges_E_Indi: num [1:92, 1:2] 85494 85495 85496 85497 85498 ...
  ..- attr(*, "dimnames")=List of 2
  .. ..$ : NULL
  .. ..$ : chr [1:2] "cell" "value"
 $ Lower-MiddleY: num [1:84, 1:2] 72598 72599 73318 73319 73320 ...
  ..- attr(*, "dimnames")=List of 2
  .. ..$ : NULL
  .. ..$ : chr [1:2] "cell" "value"
 $ Japanese     : num [1:36, 1:2] 59681 59682 60401 61121 61843 ...
  ..- attr(*, "dimnames")=List of 2
  .. ..$ : NULL
  .. ..$ : chr [1:2] "cell" "value"
 $ W_Yuman_E_Tib: num [1:131, 1:2] 72547 72548 72549 73267 73268 ...
  ..- attr(*, "dimnames")=List of 2
  .. ..$ : NULL
  .. ..$ : chr [1:2] "cell" "value"
 $ South trop ch: num [1:178, 1:2] 84818 84819 84820 84821 84822 ...
  ..- attr(*, "dimnames")=List of 2
  .. ..$ : NULL
  .. ..$ : chr [1:2] "cell" "value"
 $ Chinese_loess: num [1:258, 1:2] 62493 62494 62495 62496 62497 ...
  ..- attr(*, "dimnames")=List of 2
  .. ..$ : NULL
  .. ..$ : chr [1:2] "cell" "value"
 $ Southwes amaz: num [1:194, 1:2] 137758 137759 137760 137761 137762 ...
  ..- attr(*, "dimnames")=List of 2
  .. ..$ : NULL
  .. ..$ : chr [1:2] "cell" "value"
 $ C/S_Andes    : num [1:165, 1:2] 137736 137737 137738 137739 137740 ...
  ..- attr(*, "dimnames")=List of 2
  .. ..$ : NULL
  .. ..$ : chr [1:2] "cell" "value"
origin_vectors <- rep(NA, 3)
for(h in 1:20){
originI <- Pop[per.origin[[h]][, 1], ]
cell_vector <-  as.vector(per.origin[[h]][, 1])
x_values <- matrix(c(4:21), dim(originI)[1], 18, byrow=TRUE)
x_value_vector <- as.vector(x_values)
y_value_vector <- as.vector(originI)
all_vectors_pre <- cbind(rep(names(per.origin)[h], length(x_value_vector)),x_value_vector, y_value_vector, cell_vector)
origin_vectors <- rbind(origin_vectors, all_vectors_pre)
}
number of columns of result is not a multiple of vector length (arg 1)
#names(origin_vectors)[1] <- "location"
origin_vectors <- origin_vectors[-1,]
head(origin_vectors)
                 x_value_vector y_value_vector       cell_vector
 "W_African_Sav" "4"            "0.0665848368603154" "92506"    
 "W_African_Sav" "4"            "0.0656553166514468" "92507"    
 "W_African_Sav" "4"            "0.0640560613701872" "92508"    
 "W_African_Sav" "4"            "0.0616764490226062" "92509"    
 "W_African_Sav" "4"            "0.0603738992153177" "92510"    
 "W_African_Sav" "4"            "0.0583261699213305" "92511"    
#not in origin
All <- Pop[all_cells[, 1], ]
cell_vector <-  as.vector(All[, 1])
x_values <- matrix(c(4:21), dim(All)[1], 18, byrow=TRUE)
x_value_vector <- as.vector(x_values)
y_value_vector <- as.vector(All)
all_vectors_pre <- cbind(rep("not_origin", length(x_value_vector)),x_value_vector, y_value_vector, cell_vector)
all_vectors <- as.data.frame(rbind(origin_vectors, all_vectors_pre))
levels(all_vectors[,1])
 [1] "C/S_Andes"     "Chinese_loess" "E_North_Ameri" "Ethipian plat" "Fertile_Cresc"
 [6] "Ganges_E_Indi" "Japanese"      "Lower-MiddleY" "Mesoamerica"   "N_Lowland_SA" 
[11] "New_Guinea"    "not_origin"    "NW_Lowland_SA" "S_India"       "Sava_W_India" 
[16] "South trop ch" "Southwes amaz" "Sudanic_Savan" "W_African_Sav" "W_Yuman_E_Tib"
[21] "West Africa T"
all_vectors <- cbind(all_vectors, scale(as.numeric(all_vectors[,3])))
colnames(all_vectors) <- c("location_name", "x_values", "density_values", "cell_ID", "scaled_density_values")
all_vectors <- all_vectors[, c(1,2,3,5,4)]
dim(all_vectors)
[1] 3732480       5
origins <- subset(all_vectors, location_name != "not_origin")
par(mfrow=c(4,6), mar=c(0,0,0,0), xaxt="n")
for(h in 1:20){
#h <- 3
density_trend <- origins[which(origins[,1] == names(per.origin)[h]),]
plot(as.numeric(density_trend[,2]), as.numeric(density_trend[,4]), col=adjustcolor("cornflowerblue", alpha=0.8),  xlab="year", ylab="Density", ylim=c(-5,5), xlim=c(4,21), type="n")
points(as.numeric(density_trend[,2]), as.numeric(density_trend[,4]), cex=0.5, col=adjustcolor("cornflowerblue", 0.5))
mtext(as.character(density_trend[1,1]), 3, line=-1, cex=0.5)
}
#all origins
plot(as.numeric(density_trend[,2]), as.numeric(density_trend[,4]), col=adjustcolor("cornflowerblue", alpha=0.8),  xlab="year", ylab="Density", ylim=c(-5,5), xlim=c(4,21), type="n")
points(as.numeric(origins[,2]), as.numeric(origins[,4]), cex=0.5, col=adjustcolor("cornflowerblue", 0.5))
mtext("all origins", 3, line=-1, cex=0.5)
#not origins
not_origins <- subset(all_vectors, location_name == "not_origin")
plot(as.numeric(not_origins[,2]), as.numeric(not_origins[,4]), col=adjustcolor("cornflowerblue", alpha=0.8),  xlab="year", ylab="Density", ylim=c(-5,5), xlim=c(4,21), type="n")
points(as.numeric(not_origins[,2]), as.numeric(not_origins[,4]), cex=0.5, col=adjustcolor("cornflowerblue", 0.5))
mtext("everything other than origins", 3, line=-1, cex=0.5)

par(mfrow=c(4,6), mar=c(0,0,0,0), xaxt="n")
for(h in 1:20){
#h <- 3
density_trend <- origins[which(origins[,1] == names(per.origin)[h]),]
plot(as.numeric(density_trend[,2]), as.numeric(density_trend[,4]), col=adjustcolor("cornflowerblue", alpha=0.8),  xlab="year", ylab="Density", ylim=c(-2,2), xlim=c(4,21), type="n")
for(g in levels(as.factor(density_trend[,5]))){
subbed <- subset(density_trend, cell_ID == g)
ordered_subbed <- subbed[order(subbed$x_values), ]
lines(as.numeric(ordered_subbed[,2]), as.numeric(ordered_subbed[,4]), lwd=.3, col=adjustcolor("cornflowerblue", 0.8))
}
mtext(as.character(density_trend[1,1]), 3, line=-1, cex=0.5)
}

par(mfrow=c(4,6), mar=c(0,0,0,0))
for(h in 1:20){
#h <- 3
density_trend <- all_vectors[which(all_vectors[,1] == names(per.origin)[h]),]
plot(as.numeric(density_trend[,2]), as.numeric(density_trend[,4]), col=adjustcolor("cornflowerblue", alpha=0.8),  xlab="year", ylab="Density", ylim=c(-3,3), xlim=c(4,21), type="n", xaxt="n", yaxt="n")
points(as.numeric(density_trend[,2]), as.numeric(density_trend[,4]), cex=0.5, col=adjustcolor("grey", 0.5))
ordered_density_trend <- density_trend[order(density_trend$x_values), ]
gammer <- loess(as.numeric(ordered_density_trend[,4]) ~ as.numeric(ordered_density_trend[,2]))
summary(gammer)
lines(as.numeric(ordered_density_trend[,2]) ,predict(gammer),  col="cornflowerblue", lwd=2)
mtext(as.character(ordered_density_trend[1,1]), 3, line=-1, cex=0.5)
}
density_trend <- all_vectors[-1,]
plot(as.numeric(density_trend[,2]), as.numeric(density_trend[,4]), col=adjustcolor("cornflowerblue", alpha=0.8),  xlab="year", ylab="Density", ylim=c(-3,3), xlim=c(4,21), type="n", xaxt="n", yaxt="n")
points(as.numeric(density_trend[,2]), as.numeric(density_trend[,4]), cex=0.5, col=adjustcolor("grey", 0.5))
ordered_density_trend <- density_trend[order(density_trend$x_values), ]
gammer <- loess(as.numeric(ordered_density_trend[,4]) ~ as.numeric(ordered_density_trend[,2]))

span
the parameter α which controls the degree of smoothing.

The size of the neighbourhood is controlled by α (set by span or enp.target). For α < 1, the neighbourhood includes proportion α of the points, and these have tricubic weighting (proportional to (1 - (dist/maxdist)3)3). For α > 1, all points are used, with the ‘maximum distance’ assumed to be α^(1/p) times the actual maximum distance for p explanatory variables.

par(mfrow=c(4,6), mar=c(0,0,0,0))
for(h in 1:20){
#h <- 3
density_trend <- all_vectors[which(all_vectors[,1] == names(per.origin)[h]),]
ordered_density_trend <- density_trend[order(density_trend$x_values), ]

gammer <- loess(as.numeric(ordered_density_trend[,4]) ~ as.numeric(ordered_density_trend[,2]), span = .5)
summary(gammer)
predict_gam <- predict(gammer, se=TRUE)

plot(as.numeric(density_trend[,2]), as.numeric(density_trend[,4]), col=adjustcolor("cornflowerblue", alpha=0.8),  xlab="year", ylab="Density", ylim=c(-2,2), xlim=c(0,21), type="n", xaxt="n", yaxt="n")
#axis(1, label= seq(4,21, by=1), at=rev(seq(1,18, by=1)))

polygon(x=22-c(Start_of_early_window, Start_of_early_window, End_of_early_window_start_of_late_window, End_of_early_window_start_of_late_window), y=c(-2, 2, 2, -2), col=adjustcolor("limegreen", alpha= 0.2), border=adjustcolor("limegreen", alpha= 0.9))

polygon(x=22-c( End_of_early_window_start_of_late_window, End_of_early_window_start_of_late_window, End_of_late_window, End_of_late_window), y=c(-2, 2, 2, -2), col=adjustcolor("firebrick", alpha= 0.2), border=adjustcolor("firebrick", alpha= 0.9))

abline(h=0, lty=2, col="grey")

lines(ordered_density_trend[,2] ,predict_gam$fit,  col="cornflowerblue")
lines(ordered_density_trend[,2] ,  predict_gam$fit + predict_gam$se.fit,  col="grey", lty=1)
lines(ordered_density_trend[,2] ,  predict_gam$fit - predict_gam$se.fit,  col="grey", lty=1)


mtext(as.character(ordered_density_trend[1,1]), 3, line=-1, cex=0.5)
}
par(mfrow=c(2,5), mar=c(0,0,0,0))
for(j in seq(0.2, 2, by=.2)){

plot(as.numeric(density_trend[,2]), as.numeric(density_trend[,4]), col=adjustcolor("cornflowerblue", alpha=0.8),  xlab="year", ylab="Density", ylim=c(-3,3), xlim=c(0,21), type="n", xaxt="n")
axis(1, label= seq(4,21, by=1), at=rev(seq(1,18, by=1)))


polygon(x=22-c(Start_of_early_window, Start_of_early_window, End_of_early_window_start_of_late_window, End_of_early_window_start_of_late_window), y=c(-2, 2, 2, -2), col=adjustcolor("limegreen", alpha= 0.2), border=adjustcolor("limegreen", alpha= 0.9))

polygon(x=22-c( End_of_early_window_start_of_late_window, End_of_early_window_start_of_late_window, End_of_late_window, End_of_late_window), y=c(-2, 2, 2, -2), col=adjustcolor("firebrick", alpha= 0.2), border=adjustcolor("firebrick", alpha= 0.9))


for(h in 1:18){
#h <- 3
density_trend <- all_vectors[which(all_vectors[,1] == levels(all_vectors[,1])[h]),]
ordered_density_trend <- density_trend[order(density_trend$x_values), ]

gammer <- loess(as.numeric(ordered_density_trend[,4]) ~ as.numeric(ordered_density_trend[,2]), span = j)
summary(gammer)
predict_gam <- predict(gammer, se=TRUE)

abline(h=0, lty=2, col="grey")

lines(ordered_density_trend[,2] ,predict_gam$fit,  col="cornflowerblue")

}
mtext(paste("span = ", j), 3, line=-3, cex=0.5)
}
# need to add a global mean, an everything but the origins mean, and a buffer around the origins mean. 
# Function standardization
std <- function(x) {
  b <- (x - min(x)) / (max(x) - min(x))
  return(rev(b))
}


diff_df <- function(h){ 
# Calculating mean and 
global.means <- global.SD <- list()

for (j in 1:length(per.origin)) {
  #print(j)
  originI <- Pop[per.origin[[j]][, 1], ]
  time <- 21:4
  originI <- na.exclude(originI)
  b <- apply(originI, 1, std)
  nJ <- nrow(originI)
  predictions <- matrix(nrow = nJ, ncol = length(time))
  colnames(predictions) <- as.character(time)
  for(i in 1:nJ) {
    
    # Need to show a gradient of these df values. 
    model <- gam(b[, i] ~ s(time, df = h))
    col <- sample(rainbow(100), 1)
    predictions[i, ] <- predict(model)
    #plot(b[, i] ~ time)
    #lines(predictions[i, ] ~ time)
  }
  global.means[[j]] <- apply(predictions, 2, mean) 
  global.SD[[j]] <- apply(predictions, 2, sd)
}


names(global.means) <- names(per.origin)
names(global.SD) <- names(per.origin)

return(list(global.means, global.SD))
}
means_matrix <- matrix(rep(NA,19*20), 20, 19)
colnames(means_matrix) <- c("origin", rev(seq(4, 21, by=1)))
means_matrix[,1] <- names(global.means)
for(i in 1:20){
means_matrix[i,2:19] <- global.means[[i]]
}
#global.SD

SD_matrix <- matrix(rep(NA,19*20), 20, 19)
colnames(SD_matrix) <- c("origin", rev(seq(4, 21, by=1)))
SD_matrix[,1] <- names(global.SD)
for(i in 1:20){
SD_matrix[i,2:19] <- global.SD[[i]]
}
par(mfrow=c(4,5), mar=c(0,0,0,0))

for_3 <- diff_df(1)
global.means <- for_3[[1]]
global.SD <- for_3[[2]]

for(i in 1:20){
  

plot(4:21, global.means[[i]], col=adjustcolor("cornflowerblue", alpha=0.8),  xlab="year", ylab="Density", xaxt="n", type="n", ylim=c(0,1), xlim=c(0,22), xaxt="n", yaxt="n")

polygon(x=22-c(Start_of_early_window, Start_of_early_window, End_of_early_window_start_of_late_window, End_of_early_window_start_of_late_window) , y=c(-1, 2, 2, -1), col=adjustcolor("limegreen", alpha= 0.2), border=adjustcolor("limegreen", alpha= 0.9))

polygon(x= 22-c( End_of_early_window_start_of_late_window, End_of_early_window_start_of_late_window, End_of_late_window, End_of_late_window) , y=c(-1, 2, 2, -1), col=adjustcolor("firebrick", alpha= 0.2), border=adjustcolor("firebrick", alpha= 0.9))  
  
polygon(c(4:21,21:4) -3 , c(global.means[[i]] + abs(global.SD[[i]]), rev(global.means[[i]] - abs(global.SD[[i]]))), col="cornflowerblue")
lines(global.means[[i]])

#axis(1, at=seq(1,18, by=1), label=rev(seq(4, 21, by=1)))
mtext(names(global.means)[i], 3, line=-1, cex=.5, adj=.3)
}

GAM model using one degree of freedom

par(mfrow=c(4,5), mar=c(0,0,0,0))

for_3 <- diff_df(2)
global.means <- for_3[[1]]
global.SD <- for_3[[2]]

for(i in 1:20){
plot(4:21, global.means[[i]], col=adjustcolor("cornflowerblue", alpha=0.8),  xlab="year", ylab="Density", xaxt="n", type="n", ylim=c(0,1), xlim=c(0,22), xaxt="n", yaxt="n")

polygon(x=22-c(Start_of_early_window, Start_of_early_window, End_of_early_window_start_of_late_window, End_of_early_window_start_of_late_window) , y=c(-1, 2, 2, -1), col=adjustcolor("limegreen", alpha= 0.2), border=adjustcolor("limegreen", alpha= 0.9))

polygon(x= 22-c( End_of_early_window_start_of_late_window, End_of_early_window_start_of_late_window, End_of_late_window, End_of_late_window) , y=c(-1, 2, 2, -1), col=adjustcolor("firebrick", alpha= 0.2), border=adjustcolor("firebrick", alpha= 0.9))

polygon(c(4:21,21:4) -3 , c(global.means[[i]] + abs(global.SD[[i]]), rev(global.means[[i]] - abs(global.SD[[i]]))), col="cornflowerblue")
lines(global.means[[i]])

#axis(1, at=seq(1,18, by=1), label=rev(seq(4, 21, by=1)))
mtext(names(global.means)[i], 3, line=-1, cex=.5, adj=.3)
}

GAM model using two degree of freedom

par(mfrow=c(4,5), mar=c(0,0,0,0))

for_3 <- diff_df(3)
global.means <- for_3[[1]]
global.SD <- for_3[[2]]

for(i in 1:20){
plot(4:21, global.means[[i]], col=adjustcolor("cornflowerblue", alpha=0.8),  xlab="year", ylab="Density", xaxt="n", type="n", ylim=c(0,1), xlim=c(0,22), xaxt="n", yaxt="n")

polygon(x=22-c(Start_of_early_window, Start_of_early_window, End_of_early_window_start_of_late_window, End_of_early_window_start_of_late_window) , y=c(-1, 2, 2, -1), col=adjustcolor("limegreen", alpha= 0.2), border=adjustcolor("limegreen", alpha= 0.9))

polygon(x= 22-c( End_of_early_window_start_of_late_window, End_of_early_window_start_of_late_window, End_of_late_window, End_of_late_window) , y=c(-1, 2, 2, -1), col=adjustcolor("firebrick", alpha= 0.2), border=adjustcolor("firebrick", alpha= 0.9))

polygon(c(4:21,21:4) -3 , c(global.means[[i]] + abs(global.SD[[i]]), rev(global.means[[i]] - abs(global.SD[[i]]))), col="cornflowerblue")
lines(global.means[[i]])

#axis(1, at=seq(1,18, by=1), label=rev(seq(4, 21, by=1)))
mtext(names(global.means)[i], 3, line=-1, cex=.5, adj=.3)
}

GAM model using three degree of freedom

par(mfrow=c(4,5), mar=c(0,0,0,0))

for_3 <- diff_df(17)
global.means <- for_3[[1]]
global.SD <- for_3[[2]]

for(i in 1:20){
plot(4:21, global.means[[i]], col=adjustcolor("cornflowerblue", alpha=0.8),  xlab="year", ylab="Density", xaxt="n", type="n", ylim=c(0,1), xlim=c(0,22), xaxt="n", yaxt="n")

polygon(x=22-c(Start_of_early_window, Start_of_early_window, End_of_early_window_start_of_late_window, End_of_early_window_start_of_late_window) , y=c(-1, 2, 2, -1), col=adjustcolor("limegreen", alpha= 0.2), border=adjustcolor("limegreen", alpha= 0.9))

polygon(x= 22-c( End_of_early_window_start_of_late_window, End_of_early_window_start_of_late_window, End_of_late_window, End_of_late_window) , y=c(-1, 2, 2, -1), col=adjustcolor("firebrick", alpha= 0.2), border=adjustcolor("firebrick", alpha= 0.9))   
  
polygon(c(4:21,21:4) -3 , c(global.means[[i]] + abs(global.SD[[i]]), rev(global.means[[i]] - abs(global.SD[[i]]))), col="cornflowerblue")
lines(global.means[[i]])

#axis(1, at=seq(1,18, by=1), label=rev(seq(4, 21, by=1)))
mtext(names(global.means)[i], 3, line=-1, cex=.5, adj=.3)
}

GAM model using 17 degree of freedom

Productivity

# Load patricks productivity PCA data
load('Productivity_ALL.RDATA')

# Load origin shapefiles
origins <- readShapePoly('Origins_updated.shp')

origin.time.region <- c(2, 2, 1, 1, 1, 2, 2, 1, 2, 2, 
                        2, 2, 1, 2, 2, 2, 2, 2, 2, 2) # 1 = early; 2 = middle


# Extract the data
prod.origin <- extract(Productivity.ALL, origins)
# Mean and SD per region
means <- lapply(prod.origin, colMeans, na.rm = TRUE)
sds <- lapply(prod.origin, sd, na.rm = TRUE)
names(means) <- origins@data$CONTINENT
ymax <- max(unlist(means))
ymin <- min(unlist(means))
time <- 4:21

# Plot
#pdf("productivity.pdf", 20, 30) 
par(mfrow = c(5, 4), mar = c(2, 2, 2, 0))
for (i in 1:length(means)) {
  plot(y = means[[i]], x = time, xlim = c(21, 4), ylim = c(ymin, ymax),
       main = names(means)[i], cex.main = 1, cex.lab = 1, cex.axis = 1,
       ylab = "Productivity (PCA axis)", xlab = "Thousand of years ago (k)",
       pch = 20, lwd = 1, type = "l", 
       col = c("purple", "green")[origin.time.region[i]])
  up <- sds[[i]] + means[[i]]
  down <-  means[[i]] - sds[[i]]
  lines(up ~ time, lty = 2)
  lines(down ~ time, lty = 2)
  
}
#dev.off()

Compare rates between origins and not-origins


hist(as.numeric(not_origins[,4]), col=adjustcolor("cornflowerblue", alpha=.5),  breaks=100)
hist(as.numeric(origins[,4]), add=TRUE, col=adjustcolor("limegreen", alpha=.5), breaks=100)

Compare rates between different time periods

Setup final figure

Frame in the layout

a <- layout(matrix(c(
    1, 1, 1, 1, 1, 1, 1, 1,
    3,  6, 7, 8, 9, 10, 11, 4, 
    3,  5, 5, 5, 5, 5, 5,   4, 
    3,  12, 13, 14, 15, 16, 17, 4,
    2, 2, 2, 2, 2, 2, 2, 2
    ), 5, 8, byrow=TRUE), width=c(1, 1, 1, 1, 1, 1, 1, 1), height=c(0.5, 1, 1.5, 1, 0.5))
layout.show(a)

Make blank template plots

frameplot <- function(){
    plot(21:0,rep(0, 22), xlim=c(21,0), ylim=c(-3, 2.25), type="n", xaxt="n", yaxt="n", xlab="", ylab="")
}

frameplot_bottom <- function(){
    plot(21:0,rep(0, 22), xlim=c(21,0), ylim=c(-3.25, 2), type="n", xaxt="n", yaxt="n", xlab="", ylab="")
}

frameplot_flex <- function(min, max){
    plot(21:0,rep(0, 22), xlim=c(21,0), ylim=c(min, max), type="n", xaxt="n", yaxt="n", xlab="", ylab="")
}

frameplot_bottom_flex <- function(min, max){
    plot(21:0,rep(0, 22), xlim=c(21,0), ylim=c(min, max), type="n", xaxt="n", yaxt="n", xlab="", ylab="")
}

Trend through time panel

Setup the plot template for small panel plots (#6-17 on layout panel)

#plot type 1
lines_scaled_loess_no_zoom <- function(all_vectors, location){
  #h <- 3

  
origins <- subset(all_vectors, location_name != "not_origins")
density_trend <- origins[which(origins[,1] == location ),]

#plot(as.numeric(density_trend[,2]), as.numeric(density_trend[,4]), col=adjustcolor("cornflowerblue", alpha=0.8),  xlab="year", ylab="Density", ylim=c(-2,2), xlim=c(4,21), type="n")
g <- 67404
for(g in levels(as.factor(as.character(density_trend[,5])))){
subbed <- subset(density_trend, cell_ID == as.numeric(g))
subbed <- as.data.frame(subbed)
class(subbed[,2]) <- "numeric"
ordered_subbed <- subbed[order(subbed$x_values), ]
lines(3+rev(ordered_subbed$x_values), as.numeric(ordered_subbed[,4]), lwd=.3, col=adjustcolor("limegreen", 0.3))
}



ordered_density_trend <- density_trend[order(density_trend$x_values), ]

gammer <- loess(as.numeric(ordered_density_trend[,4]) ~ as.numeric(ordered_density_trend[,2]), span = 1)
summary(gammer)
predict_gam <- predict(gammer, se=TRUE)



abline(h=0, lty=2, col="grey")


#lines(rev(ordered_density_trend[,2]) ,  predict_gam$fit + predict_gam$se.fit,  col="orange", lty=1, lwd=1)
#lines(rev(ordered_density_trend[,2]) ,  predict_gam$fit - predict_gam$se.fit,  col="orange", lty=1, lwd=1)


lines(3+rev(as.numeric(ordered_density_trend[,2])) ,predict_gam$fit,  col="firebrick", lwd=1)


  
}


frameplot()
lines_scaled_loess_no_zoom(all_vectors, "Fertile_Cresc")
axis(2)
#plot type 2
location <- "Fertile_Cresc"

lines_not_scaled_loess_no_zoom <- function(all_vectors, location){
  #plot type 1

origins <- subset(all_vectors, location_name != "not_origins")
density_trend <- origins[which(origins[,1] == location ),]

#plot(as.numeric(density_trend[,2]), as.numeric(density_trend[,4]), col=adjustcolor("cornflowerblue", alpha=0.8),  xlab="year", ylab="Density", ylim=c(-2,2), xlim=c(4,21), type="n")
g <- 67404
for(g in levels(as.factor(as.character(density_trend[,5])))){
subbed <- subset(density_trend, cell_ID == as.numeric(g))
subbed <- as.data.frame(subbed)
class(subbed[,2]) <- "numeric"
ordered_subbed <- subbed[order(subbed$x_values), ]
lines(3+rev(ordered_subbed$x_values), as.numeric(as.character(ordered_subbed[,3])), lwd=.3, col=adjustcolor("limegreen", 0.3))
}



ordered_density_trend <- density_trend[order(density_trend$x_values), ]

gammer <- loess(as.numeric(as.character(ordered_density_trend[,3])) ~ as.numeric(ordered_density_trend[,2]), span = 1)
summary(gammer)
predict_gam <- predict(gammer, se=TRUE)



abline(h=0, lty=2, col="grey")


#lines(rev(ordered_density_trend[,2]) ,  predict_gam$fit + predict_gam$se.fit,  col="orange", lty=1, lwd=1)
#lines(rev(ordered_density_trend[,2]) ,  predict_gam$fit - predict_gam$se.fit,  col="orange", lty=1, lwd=1)


lines(3+rev(as.numeric(ordered_density_trend[,2])) ,predict_gam$fit,  col="firebrick", lwd=1)


}

    frameplot()
lines_not_scaled_loess_no_zoom(all_vectors, "Fertile_Cresc")
   axis(2)
  
#plot type 3
polygon_scaled_loess_no_zoom <- function(all_vectors, location){
 
  
 origin <- subset(all_vectors, location_name == location)
 

#plot(as.numeric(density_trend[,2]), as.numeric(density_trend[,4]), col=adjustcolor("cornflowerblue", alpha=0.8),  xlab="year", ylab="Density", ylim=c(-2,2), xlim=c(4,21), type="n")
g <- 67404
for(g in levels(as.factor(as.character(origin[,5])))){
subbed <- subset(origin, cell_ID == as.numeric(g))
subbed <- as.data.frame(subbed)
class(subbed[,2]) <- "numeric"
ordered_subbed <- subbed[order(subbed$x_values), ]
lines(3+rev(ordered_subbed$x_values), as.numeric(ordered_subbed[,4]), lwd=.3, col=adjustcolor("limegreen", 0.3))
}


levs <- levels(as.factor(as.character(origin[,5])))



predictions <- matrix(rep(NA, length(levs)* 18), length(levs), 18)
colnames(predictions) <- as.character(4:21)

g <- "67404"
i <- 1
for(g in levs){
subbed <- subset(origin, cell_ID == as.numeric(as.character(g)))

model <- loess(as.numeric(subbed[,4]) ~ as.numeric(subbed[,2]), span = .5)
    
    predictions[i, ] <- predict(model, 1:18)
i <- i + 1
    }

m <- colMeans(predictions)
sd <- colSds(predictions)

global.means <- m
global.SD <- sd

i <- which(names(global.means) == location)

polygon(c( 21:4, 4:21) , c(global.means + abs(global.SD), rev(global.means - abs(global.SD))), col= adjustcolor("firebrick", alpha=.5), border=NA)
lines(21:4 ,global.means, col="firebrick", lwd=2)



abline(h=0, lty=2, col="grey")


#lines(rev(ordered_density_trend[,2]) ,  predict_gam$fit + predict_gam$se.fit,  col="orange", lty=1, lwd=1)
#lines(rev(ordered_density_trend[,2]) ,  predict_gam$fit - predict_gam$se.fit,  col="orange", lty=1, lwd=1)


#lines(3+rev(as.numeric(ordered_density_trend[,2])) ,predict_gam$fit,  col="firebrick", lwd=1)

 
  
}

frameplot()
polygon_scaled_loess_no_zoom(all_vectors, "E_North_Ameri")
#plot type 4
polygon_scaled_GAM_no_zoom <- function(all_vectors, location){
 
  
 origin <- subset(all_vectors, location_name == location)
 

#plot(as.numeric(density_trend[,2]), as.numeric(density_trend[,4]), col=adjustcolor("cornflowerblue", alpha=0.8),  xlab="year", ylab="Density", ylim=c(-2,2), xlim=c(4,21), type="n")
g <- 67404
for(g in levels(as.factor(as.character(origin[,5])))){
subbed <- subset(origin, cell_ID == as.numeric(g))
subbed <- as.data.frame(subbed)
class(subbed[,2]) <- "numeric"
ordered_subbed <- subbed[order(subbed$x_values), ]
lines(3+rev(ordered_subbed$x_values), as.numeric(ordered_subbed[,4]), lwd=.3, col=adjustcolor("limegreen", 0.3))
}


levs <- levels(as.factor(as.character(origin[,5])))



predictions <- matrix(rep(NA, length(levs)* 18), length(levs), 18)
colnames(predictions) <- as.character(4:21)

g <- "67404"
i <- 1
for(g in levs){
subbed <- subset(origin, cell_ID == as.numeric(as.character(g)))

model <- gam(as.numeric(as.character(subbed[,4])) ~ s(as.numeric(as.character(subbed[,2])), df = 3)) 
    
    predictions[i, ] <- predict(model)
i <- i + 1
    }

m <- colMeans(predictions)
sd <- colSds(predictions)

global.means <- m
global.SD <- sd

i <- which(names(global.means) == location)

polygon(c( 4:21,  21:4) , c(global.means + abs(global.SD), rev(global.means - abs(global.SD))), col= adjustcolor("firebrick", alpha=.5), border=NA)
lines(4:21 ,global.means, col="firebrick", lwd=2)



abline(h=0, lty=2, col="grey")


#lines(rev(ordered_density_trend[,2]) ,  predict_gam$fit + predict_gam$se.fit,  col="orange", lty=1, lwd=1)
#lines(rev(ordered_density_trend[,2]) ,  predict_gam$fit - predict_gam$se.fit,  col="orange", lty=1, lwd=1)


#lines(3+rev(as.numeric(ordered_density_trend[,2])) ,predict_gam$fit,  col="firebrick", lwd=1)

 
  
}

frameplot()
polygon_scaled_GAM_no_zoom(all_vectors, "E_North_Ameri")
#plot type 5
polygon_scaled_loess_zoom <- function(all_vectors, location){
 
  
 origin <- subset(all_vectors, location_name == location)
 

#plot(as.numeric(density_trend[,2]), as.numeric(density_trend[,4]), col=adjustcolor("cornflowerblue", alpha=0.8),  xlab="year", ylab="Density", ylim=c(-2,2), xlim=c(4,21), type="n")
g <- 67404
for(g in levels(as.factor(as.character(origin[,5])))){
subbed <- subset(origin, cell_ID == as.numeric(g))
subbed <- as.data.frame(subbed)
class(subbed[,2]) <- "numeric"
ordered_subbed <- subbed[order(subbed$x_values), ]
lines(3+rev(ordered_subbed$x_values), as.numeric(ordered_subbed[,4]), lwd=.3, col=adjustcolor("limegreen", 0.3))
}


levs <- levels(as.factor(as.character(origin[,5])))



predictions <- matrix(rep(NA, length(levs)* 18), length(levs), 18)
colnames(predictions) <- as.character(4:21)

g <- "67404"
i <- 1
for(g in levs){
subbed <- subset(origin, cell_ID == as.numeric(as.character(g)))

model <- loess(as.numeric(subbed[,4]) ~ as.numeric(subbed[,2]), span = 1)
    
    predictions[i, ] <- predict(model, 1:18)
i <- i + 1
    }

m <- colMeans(predictions)
sd <- colSds(predictions)

global.means <- m
global.SD <- sd

i <- which(names(global.means) == location)

polygon(c( 21:4, 4:21) , c(global.means + abs(global.SD), rev(global.means - abs(global.SD))), col= adjustcolor("firebrick", alpha=.5), border=NA)
lines(21:4 ,global.means, col="firebrick", lwd=2)



abline(h=0, lty=2, col="grey")


#lines(rev(ordered_density_trend[,2]) ,  predict_gam$fit + predict_gam$se.fit,  col="orange", lty=1, lwd=1)
#lines(rev(ordered_density_trend[,2]) ,  predict_gam$fit - predict_gam$se.fit,  col="orange", lty=1, lwd=1)


#lines(3+rev(as.numeric(ordered_density_trend[,2])) ,predict_gam$fit,  col="firebrick", lwd=1)

 
  
}

origin <- subset(all_vectors, location_name == "E_North_Ameri")
y_max <- max(origin[,4], na.rm=TRUE)
y_min <- min(origin[,4], na.rm=TRUE)
frameplot_flex(y_min,y_max)
axis(2)
polygon_scaled_loess_zoom(all_vectors, "E_North_Ameri")
location <- "Chinese_loess"

gam_per_cell_not_scaled <- function(all_vectors, location){
origin <- subset(all_vectors, location_name == location)
levs <- levels(as.factor(as.character(origin[,5])))

predictions <- matrix(rep(NA, length(levs)* 18), length(levs), 18)
colnames(predictions) <- as.character(4:21)

g <- "62493"
i <- 1
for(g in levs){
subbed <- subset(origin, cell_ID == as.numeric(g))

model <- gam(as.numeric(as.character(subbed[,3])) ~ s(as.numeric(subbed[,2]), df = 3))
    
    predictions[i, ] <- predict(model)
i <- i + 1
    }

m <- colMeans(predictions)
sd <- colSds(predictions)
return(list(m, sd))

}
#plot type 5
polygon_not_scaled_gam_no_zoom <- function(all_vectors, location){
 
#frameplot()  
  for_3 <- gam_per_cell_not_scaled(all_vectors, location)
global.means <- for_3[[1]]
global.SD <- for_3[[2]]

#as.factor(all_vectors$location_name)
#location <- "Chinese_loess"

i <- which(names(global.means) == location)

polygon(c( 4:21, 21:4) , c(global.means + abs(global.SD), rev(global.means - abs(global.SD))), col= adjustcolor("limegreen", alpha=.5))
lines(4:21 ,global.means, col="limegreen", lwd=2)


  
}

    plot(21:0,rep(0, 22), xlim=c(21,0), ylim=c(-0.5, 0.5), type="n", xaxt="n", yaxt="n", xlab="", ylab="")

polygon_not_scaled_gam_no_zoom(all_vectors, "Fertile_Cresc")
   axis(2)
  
location <- "Chinese_loess"

gam_per_cell_scaled <- function(all_vectors, location){
origin <- subset(all_vectors, location_name == location)
levs <- levels(as.factor(as.character(origin[,5])))

predictions <- matrix(rep(NA, length(levs)* 18), length(levs), 18)
colnames(predictions) <- as.character(4:21)

g <- "62493"
i <- 1
for(g in levs){
subbed <- subset(origin, cell_ID == as.numeric(g))

model <- gam(as.numeric(subbed[,4]) ~ s(as.numeric(subbed[,2]), df = 3))
    
    predictions[i, ] <- predict(model)
i <- i + 1
    }

m <- colMeans(predictions)
sd <- colSds(predictions)
return(list(m, sd))

}
#plot type 6
polygon_scaled_gam_no_zoom <- function(all_vectors, location){
  
#frameplot()  
  for_3 <- gam_per_cell(all_vectors, location)
global.means <- for_3[[1]]
global.SD <- for_3[[2]]

#as.factor(all_vectors$location_name)
#location <- "Chinese_loess"

i <- which(names(global.means) == location)

polygon(c( 4:21, 21:4) , c(global.means + abs(global.SD), rev(global.means - abs(global.SD))), col= adjustcolor("limegreen", alpha=.5))
lines(4:21 ,global.means, col="limegreen", lwd=2)


  
}

frameplot()

polygon_scaled_gam_no_zoom(all_vectors, "Fertile_Cresc")
#plot type 7
polygon_scaled_gam_zoom <- function(all_vectors){
  
  
}
###################

type_number <- 8

complex_figure <- function(type_number, location_name, i, means, sds, plot_type=4){
        
#polygon(x=c(-30,-30,30,30), y=c(-30,30,30,-30), col="black")  
  
abline(h=0, lty=2)          

if(i < 6 )  polygon(x=c(12,12,8.2,8.2), y=c(-3,3,3,-3), col=adjustcolor("cornflowerblue", alpha=0.2), border=NA)                    
if(i > 5)   polygon(x=c(8.2,8.2,4.2,4.2), y=c(-3,3,3,-3), col=adjustcolor("cornflowerblue", alpha=0.2), border=NA)
            
  
    match <- domestication_times[ which(domestication_times$Region == levels(domestication_times$Region)[ type_number]), 9]
    maxer <- max(match, na.rm=TRUE)
    break_one_1 <- maxer
            break_two_1 <- maxer - quantile(j)[2]

        lines(x=c(break_one_1, break_one_1), y=c(-3,3), col=adjustcolor("cornflowerblue", alpha=0.7), lwd=3, lend = 2)  

  
  
  match <- domestication_times[ which(domestication_times$Region == levels(domestication_times$Region)[ type_number]), 10]
    maxer <- max(match, na.rm=TRUE)
    break_one_2 <- maxer
            break_two_2 <- maxer - quantile(j)[2]
                
#   polygon(x=c(break_two_2, break_two_2, break_one_2, break_one_2), y=c(1, 2, 2, 1), col=adjustcolor("limegreen", alpha=0.5), border=NA)
        lines(x=c(break_one_2, break_one_2), y=c(-3,3), col=adjustcolor("cornflowerblue", alpha=1), lwd=3, lend = 2)    
    
        
                        
    match <- domestication_times[ which(domestication_times$Region == levels(domestication_times$Region)[ type_number]), 9]
    maxer <- max(match, na.rm=TRUE)
    j <- ecdf(maxer-match)
    print(j)
    

x_seq <- rev(c(0,seq(0, maxer, length.out=100)))
y_seq <- c(0, j(seq(0, maxer, length.out=100))) 

#lines(x_seq, y_seq, type="l", ylim=c(-1,1))
polygon(c(0, x_seq), c(0, y_seq) -3, border=NA, col=adjustcolor("white", alpha=1), lwd=.5)
polygon(c(0, x_seq), c(0, y_seq) -3, border="cornflowerblue", col=adjustcolor("cornflowerblue", alpha=.5), lwd=.5)
#abline(v= maxer - quantile(j)[2])


    match <- domestication_times[ which(domestication_times$Region == levels(domestication_times$Region)[ type_number]), 10]
    maxer <- max(match, na.rm=TRUE)
    j <- ecdf(maxer-match)
    print(j)
    

x_seq <- rev(c(0,seq(0, maxer, length.out=100)))
y_seq <- c(0, j(seq(0, maxer, length.out=100))) 

#lines(x_seq, y_seq)
polygon(c(0, x_seq), c(0, y_seq) -3, border="white", col=adjustcolor("white", alpha=1), lwd=.5)
polygon(c(0, x_seq), c(0, y_seq) -3, border="white", col=adjustcolor("cornflowerblue", alpha=1), lwd=.5)


    
        

        if(plot_type == 1){
    
lines_scaled_loess_no_zoom(all_vectors, location_name)

    }
        
        #abline(v=11)
    
        
if(plot_type == 2){
    
lines_not_scaled_loess_no_zoom(all_vectors, location_name)

    }


if(plot_type == 3){
    
polygon_scaled_loess_no_zoom(all_vectors, location_name)

    }

if(plot_type == 4){
    
polygon_scaled_GAM_no_zoom(all_vectors, location_name)

    }


if(plot_type == 5){
    

polygon_scaled_loess_zoom(all_vectors, location_name)


    }



        if(plot_type == 9){
    x <- c(means[[i]] , means[[i]]  + abs(sds[[i]]), means[[i]]  - abs(sds[[i]]))
    scaled <- scale(x , center=TRUE)
    meanss <- scaled[1:18]
    sdss_plus <- scaled[19:36]
    sdss_minus <- scaled[37:54]
    #abline(v=10, col="red")
    length(scaled)
    #lines(4:21, means[[i]] + sds[[i]])
    #polygon(x=c(4:21, 21:4), y=c(sdss_plus, rev(sdss_minus)), col=adjustcolor("firebrick", alpha=1), border="white")
    polygon(x=c(21:4,4:21), y=c(sdss_plus, rev(sdss_minus)), col=adjustcolor("firebrick", alpha=1), border="white") 
    }
    
    if(plot_type == 8){
#h <- 3
density_trend <- all_vectors[which(all_vectors[,1] == location_name ),]

#plot(as.numeric(density_trend[,2]), as.numeric(density_trend[,4]), col=adjustcolor("cornflowerblue", alpha=0.8),  xlab="year", ylab="Density", ylim=c(-2,2), xlim=c(4,21), type="n")

for(g in levels(as.factor(density_trend[,5]))){
subbed <- subset(density_trend, cell_ID == g)
ordered_subbed <- subbed[order(subbed$x_values), ]
lines(22-as.numeric(ordered_subbed[,2]), as.numeric(ordered_subbed[,4]), lwd=.3, col=adjustcolor("limegreen", 0.3))
}


density_trend <- all_vectors[which(all_vectors[,1] == location_name),]
ordered_density_trend <- density_trend[order(density_trend$x_values), ]

#gammer <- loess(as.numeric(ordered_density_trend[,4]) ~ as.numeric(ordered_density_trend[,2]), span = 1)
gammer <- gam(as.numeric(ordered_density_trend[,4]) ~ s(as.numeric(ordered_density_trend[,2]), df = 3))

summary(gammer)
predict_gam <- predict(gammer, se=TRUE)



abline(h=0, lty=2, col="grey")


#lines(rev(ordered_density_trend[,2]) ,  predict_gam$fit + predict_gam$se.fit,  col="orange", lty=1, lwd=1)
#lines(rev(ordered_density_trend[,2]) ,  predict_gam$fit - predict_gam$se.fit,  col="orange", lty=1, lwd=1)
polygon(x=3+c(rev(ordered_density_trend[,2]), ordered_density_trend[,2] ), y=c(predict_gam$fit, rev(predict_gam$fit)), col=adjustcolor("firebrick", alpha=.5),bty="n", lwd=.2)
lines(3+rev(as.numeric(ordered_density_trend[,2])) ,predict_gam$fit,  col="firebrick", lwd=1)


    }

if(plot_type == 10){
    
polygon_not_scaled_gam_no_zoom(all_vectors, location_name)

}


if(plot_type == 11){
    
polygon_scaled_gam_no_zoom(all_vectors, location_name)

    }

    

    
}
frameplot()
complex_figure(1,names(per.origin)[2], 1, global.means, global.SD) 

axis(1)
axis(2)

Assemble the figure

Assemble the figure

quartz(width=8, height=8)

layout(matrix(c(
    1, 1, 1, 1, 1, 1, 1, 1,
    3,  6, 7, 8, 9, 10, 11, 4, 
    3,  5, 5, 5, 5, 5, 5,   4, 
    3,  12, 13, 14, 15, 16, 17, 4,
    2, 2, 2, 2, 2, 2, 2, 2
    ), 5, 8, byrow=TRUE), width=c(1, 1, 1, 1, 1, 1, 1, 1), height=c(0.7, 1, 1.5, 1, 0.7))


par(mar=c(0,0,0,0))

# 1-4 label margins
blankplot <- function(){
    
    plot(0,0, xlim=c(4,21), ylim=c(1, 1.25), bty="n", type="n", xaxt="n", yaxt="n", xlab="", ylab="")
}

blankplot()
blankplot()
blankplot()
blankplot()







d <- readPNG("40962.png")
dim(d)
par(mar=c(0,0,0,0))
plot(0:360,0:360,type="n",xlim=c(20,360),ylim=c(65,295), yaxt="n", xaxt="n")
rasterImage(d, -28.5, -13.5, 388, 375, interpolate=TRUE, col=d)
axis(2, label=seq(-90, 90, length.out = 19), at=seq(1, 360, length.out = 19), las=1)
mtext("latitude", 2, line=4, at=180)
abline(h=seq(1, 360, length.out = 19), col=adjustcolor("grey10", alpha= 0.4), lwd=1)
abline(h=180, col=adjustcolor("white", alpha= .5), lwd=1)




flexframe <- FALSE

for(i in 1:12){

    
  flex_1 <- function(all_vectors, locations){
    origin <- subset(all_vectors, location_name == locations)
y_max <- max(origin[,4], na.rm=TRUE)
y_min <- min(origin[,4], na.rm=TRUE)
frameplot_flex(y_min,y_max)

  }
  
  flex_2 <- function(all_vectors, locations){
    origin <- subset(all_vectors, location_name == locations)
y_max <- max(origin[,4], na.rm=TRUE)
y_min <- min(origin[,4], na.rm=TRUE)
frameplot_flex(y_min,y_max)

    
    
  }

  
    if(flexframe == FALSE){if(i > 6){frameplot()}else{frameplot_bottom()}}

    
        ## customize polygons for each graph
    if(i == 1){ #mesoamerica  #values from Larson
        
      if(flexframe == TRUE){flex_1(all_vectors, "Mesoamerica")}
            complex_figure(3, "Mesoamerica", i, means, sds)
                
    
        }
    
    
    #########
    if(i == 2 ){ #NW lowlands SA  #values from Larson
        if(flexframe == TRUE){flex_1(all_vectors, "NW_Lowland_SA")}
        complex_figure(6, "NW_Lowland_SA", i, means, sds)
    

        }
        
        #########
    if( i == 3){ #NW lowlands SA  #values from Larson
        if(flexframe == TRUE){flex_1(all_vectors, "N_Lowland_SA")}
        complex_figure(6, "N_Lowland_SA", i, means, sds)
        
        }


        #########
    if(i == 4){ #Fertile crescent aka Southwest asia  #values from Larson
        
        if(flexframe == TRUE){flex_1(all_vectors, "Fertile_Cresc")}
    complex_figure(8, "Fertile_Cresc", i, means, sds)
                
        }
        
        #########
    if(i == 5){ #loess plateau  #values from Larson
        if(flexframe == TRUE){flex_1(all_vectors, "Chinese_loess")}
        complex_figure(2, "Chinese_loess", i, means, sds)
            
        }
        
        
        #########
    if(i == 6){ #new guinea  #values from Larson
        if(flexframe == TRUE){flex_1(all_vectors, "New_Guinea" )}
        complex_figure(4, "New_Guinea", i, means, sds)
        
        }


#########
    if(i == 7){ #Eastern N.A.  #values from Larson
        if(flexframe == TRUE){flex_2(all_vectors, "E_North_Ameri")}
        complex_figure(5, "E_North_Ameri", i, means, sds)
        
            }


        #########
    if(i == 8){ #Andes  #values from Larson
        if(flexframe == TRUE){flex_2(all_vectors, "C/S_Andes")}
        complex_figure(6, "C/S_Andes", i, means, sds)
        
                }


#########
    if(i == 9){ #W. African Sav  #values from Larson
        if(flexframe == TRUE){flex_2(all_vectors, "W_African_Sav")}
        complex_figure(1, "W_African_Sav", i, means, sds)
        
            }


#########
    if(i == 10){ #Sudanic sav  #values from Larson
        if(flexframe == TRUE){flex_2(all_vectors, "Sudanic_Savan")}
        complex_figure(1, "Sudanic_Savan", i, means, sds)
        
                }


#########
    if(i == 11){ #Ganges  #values from Larson
        
        if(flexframe == TRUE){flex_2(all_vectors, "Ganges_E_Indi")}
        complex_figure(7, "Ganges_E_Indi", i, means, sds) 
        
        }


#########
    if(i == 12){ #"Lower-MiddleY"  #values from Larson
        if(flexframe == TRUE){flex_2(all_vectors, "Lower-MiddleY")}
        complex_figure(2, "Lower-MiddleY", i, means, sds)
         
                }

        
        
        #lines(4:21, means[[i]])
        
        #abline(h = 1, col=adjustcolor("forestgreen", alpha=.5), lty=2)
        
    # add axes to some locations
    if(i == 1 | i == 7){axis(2, at=seq(-2,2, by=0.5), label=seq(-2,2, by=0.5), las=1)}
    if(i == 6 | i == 12){axis(4, at=seq(-2,2, by=0.5), label=seq(-2,2, by=0.5), las=1)}
    #if(i == 6 | i == 12){axis(4, at=seq(2,3, by=0.25), label=seq(0,1, by=0.25), las=1)
    #   axis(4, at=seq(-1,0, by=0.25), label=rev(seq(0,1, by=0.25)), las=1)
    #   }
    if(i > 6){axis(1)} else{axis(3)}

    
    # add text 
    if(i < 7){polygon(x=c(-30, -30, 30, 30), y=c(-3.1, -3.5, -3.5, -3.1), col="black")
    mtext(name_vector[i], 1, line=-0.9, col="white", cex=0.5)}
    
    if(i > 6){polygon(x=c(-30, -30, 30, 30), y=c(2.1, 2.5, 2.5, 2.1), col="black")
    mtext(name_vector[i], 3, line=-0.8, col="white", cex=0.5)}
    
    # add axis labels
    if(i == 1 | i ==  7){mtext("scaled density potential", 2, line=4, at=1)}
    if(i ==  3){mtext("Thousand years before present", 3, line=3.5, at =5)}
    if(i ==  9){mtext("Thousand years before present", 1, line=3.5, at =5)
        
        }
    
}






saveToPDF <- function(...) {
    d = dev.copy(pdf,...)
    dev.off(d)
}

saveToPNG <- function(...) {
    d = dev.copy(png,...)
    dev.off(d)
}

## Try them out

saveToPDF("my.pdf", height=8,width=8)
saveToPNG("my.png", height=8, width=8, units="in", res=300)
dev.off()
LS0tCnRpdGxlOiAiT3JpZ2lucyBvZiBhZ3JpY3VsdHVyZSBodW1hbiBkZW5zaXR5IGFuYWx5c2lzIgphdXRob3I6ICJUeSBUdWZmLCBCcnVubyBWaWxlbGEsIGFuZCBDYXJsb3MgQm90ZXJvIgpkYXRlOiAncHJvamVjdCBiZWdhbjogU2VwdGVtYmVyIDIwMTYsIGRvY3VtZW50IHVwZGF0ZWQ6IGByIHN0cmZ0aW1lKFN5cy50aW1lKCksIGZvcm1hdAogID0gIiVkICVCICVZIilgJwpvdXRwdXQ6CiAgcGRmX2RvY3VtZW50OiBkZWZhdWx0CiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdAogIHdvcmRfZG9jdW1lbnQ6IGRlZmF1bHQKLS0tCiMjIEN1cnJlbnQgYmVzdCB2ZXJzaW9uCiFbRmlndXJlIDEgLSBUaGlzIGZpZ3VyZXMgc2hvd3MuLi5dKG15LnBuZykKCgoKCgojIyBDb2RlCjEuIFtSIGVudmlyb25tZW50IHNldHVwXSgjci1lbnZpcm9ubWVudC1zZXR1cCkKMi4gW1NldHRpbmcgdGltZSBicmVha3NdKCNzZXR0aW5nLXRpbWUtYnJlYWtzKQozLiBbRGVmaW5pbmcgb3JpZ2luc10oI2RlZmluaW5nLW9yaWdpbnMpCjQuIFtNYXAgZm9yIGZpbmFsIGZpZ3VyZV0oI21hcC1mb3ItZmluYWwtZmlndXJlKQo1LiBbSW1wb3J0IHJhc3RlciBkYXRhXSgjaW1wb3J0LXJhc3Rlci1kYXRhKQo2LiBbR0FNIHZzLiBMb2VzcyBzbW9vdGhpbmcgbW9kZWxzXSgjZ2FtLXZzLWxvZXNzLXNtb290aGluZy1tb2RlbHMpCjcuIFtDb21wYXJlIHJhdGVzIGJldHdlZW4gb3JpZ2lucyBhbmQgbm90LW9yaWdpbnNdKCNjb21wYXJlLXJhdGVzLWJldHdlZW4tb3JpZ2lucy1hbmQtbm90LW9yaWdpbnMpCjguIFtDb21wYXJlIHJhdGVzIGJldHdlZW4gZGlmZmVyZW50IHRpbWUgcGVyaW9kc10oI2NvbXBhcmUtcmF0ZXMtYmV0d2Vlbi1kaWZmZXJlbnQtdGltZS1wZXJpb2RzKQo5LiBbU2V0dXAgZmluYWwgZmlndXJlXSgjc2V0dXAtZmluYWwtZmlndXJlKQoxMC4gW1RyZW5kIHRocm91Z2ggdGltZSBwYW5lbCBmb3IgZmluYWwgZmlndXJlXSgjdHJlbmQtdGhyb3VnaC10aW1lLXBhbmVsKQoxMS4gW0Fzc2VtYmxlIGFuZCBwcmludCBmaW5hbCBmaWd1cmVdKCNhc3NlbWJsZS10aGUtZmlndXJlKQoKCgojIyBSIGVudmlyb25tZW50IHNldHVwCiMjIyMgQXR0YWNoIGxpYnJhcmllcwpgYGB7cn0KbGlicmFyeShwbmcpCmxpYnJhcnkobWFwdG9vbHMpCmxpYnJhcnkocmFzdGVyKQpsaWJyYXJ5KGdhbSkKbGlicmFyeShtYXBzKQpsaWJyYXJ5KG1hcHByb2opCmxpYnJhcnkocmdsKQpsaWJyYXJ5KGtuaXRyKSAKYGBgCgoKCiMjIyMgU2V0IHdvcmtpbmcgZGlyZWN0b3J5CmBgYHtyfQpzZXR3ZCgifi9EZXNrdG9wL0JvdGVybyBwb3N0ZG9jIDIwMTYvSHVtYW4gZGVuc2l0eSBhbmQgdGhlIG9yaWdpbnMgb2YgYWdyaWN1bHR1cmUvIikKYGBgCgoKCiMjIFNldHRpbmcgdGltZSBicmVha3MKIyMjIyBEZWZpbmUgdGhlIHRpbWVzIG9mIGFncmljdWx0dXJhbCBvcmlnaW5zCiFbXShMYXJzb25fZGF0ZXMuanBnKQoKYGBge3J9CnBhcihtYXI9YygwLDAsMCwyMCkpCmQgPC0gcmVhZFBORygiTGFyc29uX2RhdGVzLnBuZyIpCnBsb3Qoc2VxKDAsMTgsIGxlbmd0aC5vdXQgPSAxOSksIHNlcSgwLDM2LCBsZW5ndGgub3V0ID0gMTkpLCB0eXBlPSJuIix5bGltPWMoMCwzNikseGxpbT1jKDAsIDE4KSwgeGF4dD0ibiIpCgpyYXN0ZXJJbWFnZShkLCAwLDAsMTgsMzYsIGludGVycG9sYXRlPVRSVUUsIGNvbD1kKQoKCgpTdGFydF9vZl9lYXJseV93aW5kb3cgPC0gMTYtMTIKRW5kX29mX2Vhcmx5X3dpbmRvd19zdGFydF9vZl9sYXRlX3dpbmRvdyA8LSA4LjIKRW5kX29mX2xhdGVfd2luZG93IDwtIDE3LTQuMgoKcG9seWdvbih4PWMoU3RhcnRfb2ZfZWFybHlfd2luZG93LCBTdGFydF9vZl9lYXJseV93aW5kb3csIEVuZF9vZl9lYXJseV93aW5kb3dfc3RhcnRfb2ZfbGF0ZV93aW5kb3csIEVuZF9vZl9lYXJseV93aW5kb3dfc3RhcnRfb2ZfbGF0ZV93aW5kb3cpLCB5PWMoMCwgMzQsIDM0LCAwKSwgY29sPWFkanVzdGNvbG9yKCJsaW1lZ3JlZW4iLCBhbHBoYT0gMC4yKSwgYm9yZGVyPWFkanVzdGNvbG9yKCJsaW1lZ3JlZW4iLCBhbHBoYT0gMC45KSkKCnBvbHlnb24oeD1jKCBFbmRfb2ZfZWFybHlfd2luZG93X3N0YXJ0X29mX2xhdGVfd2luZG93LCBFbmRfb2ZfZWFybHlfd2luZG93X3N0YXJ0X29mX2xhdGVfd2luZG93LCBFbmRfb2ZfbGF0ZV93aW5kb3csIEVuZF9vZl9sYXRlX3dpbmRvdyksIHk9YygwLCAzNCwgMzQsIDApLCBjb2w9YWRqdXN0Y29sb3IoImZpcmVicmljayIsIGFscGhhPSAwLjIpLCBib3JkZXI9YWRqdXN0Y29sb3IoImZpcmVicmljayIsIGFscGhhPSAwLjkpKQoKYGBgCgoKClRoZXNlIGRhdGVzIGFyZSBwcm92aWRlZCBpbiB0aGUgc3VwcGxpbWVudGFyeSBpbmZvcm1hdGlvbiBmb3IgdGhlIExhcnNvbiAoMjAxNCkgcGFwZXIuIEkndmUgY29waWVkIHRob3NlIHZhbHVlcyBpbnRvIGEgLmNzdiB0YWJsZSBwcm92aWRlZCBoZXJlLiAKCmBgYHtyfQpkb21lc3RpY2F0aW9uX3RpbWVzIDwtIHJlYWQuY3N2KCJEb21lc3RpY2F0aW9uIHRpbWluZyBsYXJzb24gMjAxNC5jc3YiKQoKZGltKGRvbWVzdGljYXRpb25fdGltZXMpCmBgYAoKYGBge3IsIGVjaG89RkFMU0V9CgprYWJsZShkb21lc3RpY2F0aW9uX3RpbWVzLCBjYXB0aW9uPSAiVGhpcyBpcyBvdXIgd29ybGQiKQpgYGAKCgpgYGB7cn0KcGFyKG1hcj1jKDUsNCw2LDEpKQoKZGF0ZXMgPC0gdW5saXN0KGRvbWVzdGljYXRpb25fdGltZXNbMzo4XSkKaGlzdChkYXRlcywgYnJlYWtzID0gMjIsIHhsaW09YygxNSwwKSwgeGxhYj0iSyB5ZWFycyBhZ28iLCBjb2w9YWRqdXN0Y29sb3IoImNvcm5mbG93ZXJibHVlIiwgYWxwaGE9IDAuNSksIGJvcmRlcj1hZGp1c3Rjb2xvcigiY29ybmZsb3dlcmJsdWUiLCBhbHBoYT0gMC45KSwgbWFpbj0iQWxsIGRhdGVzIGluIGRhdGFzZXQiICApCm10ZXh0KCJUaGlzIHRlbGxzIHVzIGFib3V0IGhvdyBldmVubHkgb3VyIGV2aWRlbmNlIGlzIGRpc3RyaWJ1dGVkIGluIHRpbWUiLCAzLCBsaW5lPTEpCgoKYGBgCgpgYGB7cn0KaGlzdChkYXRlcywgYnJlYWtzID0gMjIsIHhsaW09YygxNSwwKSwgeGxhYj0iVGhvdXNhbmQgeWVhcnMgYWdvIiwgY29sPWFkanVzdGNvbG9yKCJjb3JuZmxvd2VyYmx1ZSIsIGFscGhhPSAwLjUpLCBib3JkZXI9YWRqdXN0Y29sb3IoImNvcm5mbG93ZXJibHVlIiwgYWxwaGE9IDAuOSksIG1haW49IkFsbCBkYXRlcyBpbiBkYXRhc2V0IHdpdGggTGFyc29uKDIwMTQpIGRhdGUgd2luZG93cyIpCgpTdGFydF9vZl9lYXJseV93aW5kb3cgPC0gMTIKRW5kX29mX2Vhcmx5X3dpbmRvd19zdGFydF9vZl9sYXRlX3dpbmRvdyA8LSA4LjIKRW5kX29mX2xhdGVfd2luZG93IDwtIDQuMgoKcG9seWdvbih4PWMoU3RhcnRfb2ZfZWFybHlfd2luZG93LCBTdGFydF9vZl9lYXJseV93aW5kb3csIEVuZF9vZl9lYXJseV93aW5kb3dfc3RhcnRfb2ZfbGF0ZV93aW5kb3csIEVuZF9vZl9lYXJseV93aW5kb3dfc3RhcnRfb2ZfbGF0ZV93aW5kb3cpLCB5PWMoMCwgMzAsIDMwLCAwKSwgY29sPWFkanVzdGNvbG9yKCJsaW1lZ3JlZW4iLCBhbHBoYT0gMC4yKSwgYm9yZGVyPWFkanVzdGNvbG9yKCJsaW1lZ3JlZW4iLCBhbHBoYT0gMC45KSkKCnBvbHlnb24oeD1jKCBFbmRfb2ZfZWFybHlfd2luZG93X3N0YXJ0X29mX2xhdGVfd2luZG93LCBFbmRfb2ZfZWFybHlfd2luZG93X3N0YXJ0X29mX2xhdGVfd2luZG93LCBFbmRfb2ZfbGF0ZV93aW5kb3csIEVuZF9vZl9sYXRlX3dpbmRvdyksIHk9YygwLCAzMCwgMzAsIDApLCBjb2w9YWRqdXN0Y29sb3IoImZpcmVicmljayIsIGFscGhhPSAwLjIpLCBib3JkZXI9YWRqdXN0Y29sb3IoImZpcmVicmljayIsIGFscGhhPSAwLjkpKQoKaGlzdChkYXRlcywgYnJlYWtzID0gMjIsIHhsaW09YygxNSwwKSwgeGxhYj0iSyB5ZWFycyBhZ28iLCBjb2w9YWRqdXN0Y29sb3IoImNvcm5mbG93ZXJibHVlIiwgYWxwaGE9IDAuMiksIGJvcmRlcj1hZGp1c3Rjb2xvcigiY29ybmZsb3dlcmJsdWUiLCBhbHBoYT0gMC45KSwgbWFpbj0iIiwgYWRkPVRSVUUpCgptdGV4dCgiRWFybHkgSG9sb2NlbmUiLCAzLCBsaW5lID0gLTEsIGFkaj0uMykKbXRleHQoIk1pZGRsZSBIb2xvY2VuZSIsIDMsIGxpbmU9IC0xLCBhZGo9LjYpCgpgYGAKCmBgYHtyfQoKcGFyKG1mcm93PWMoMiwzKSwgbWFyPWMoNCw0LDIsMCkpCmRpbShkb21lc3RpY2F0aW9uX3RpbWVzKQpzcGVjaWZpY19kYXRlcyA8LSBkb21lc3RpY2F0aW9uX3RpbWVzWywzOjhdCgpmb3IoaSBpbiBjKDEsIDMsIDUsIDIsIDQsIDYpKXsKaGlzdChzcGVjaWZpY19kYXRlc1ssaV0sIGJyZWFrcyA9IDIyLCB4bGltPWMoMTUsMCksIHhsYWI9IlRob3VzYW5kIHllYXJzIGFnbyIsIGNvbD1hZGp1c3Rjb2xvcigiY29ybmZsb3dlcmJsdWUiLCBhbHBoYT0gMC41KSwgYm9yZGVyPWFkanVzdGNvbG9yKCJjb3JuZmxvd2VyYmx1ZSIsIGFscGhhPSAwLjkpLCBtYWluPSBuYW1lcyhzcGVjaWZpY19kYXRlcylbaV0pCgpTdGFydF9vZl9lYXJseV93aW5kb3cgPC0gMTIKRW5kX29mX2Vhcmx5X3dpbmRvd19zdGFydF9vZl9sYXRlX3dpbmRvdyA8LSA4LjIKRW5kX29mX2xhdGVfd2luZG93IDwtIDQuMgoKcG9seWdvbih4PWMoU3RhcnRfb2ZfZWFybHlfd2luZG93LCBTdGFydF9vZl9lYXJseV93aW5kb3csIEVuZF9vZl9lYXJseV93aW5kb3dfc3RhcnRfb2ZfbGF0ZV93aW5kb3csIEVuZF9vZl9lYXJseV93aW5kb3dfc3RhcnRfb2ZfbGF0ZV93aW5kb3cpLCB5PWMoMCwgMzAsIDMwLCAwKSwgY29sPWFkanVzdGNvbG9yKCJsaW1lZ3JlZW4iLCBhbHBoYT0gMC4yKSwgYm9yZGVyPWFkanVzdGNvbG9yKCJsaW1lZ3JlZW4iLCBhbHBoYT0gMC45KSkKCnBvbHlnb24oeD1jKCBFbmRfb2ZfZWFybHlfd2luZG93X3N0YXJ0X29mX2xhdGVfd2luZG93LCBFbmRfb2ZfZWFybHlfd2luZG93X3N0YXJ0X29mX2xhdGVfd2luZG93LCBFbmRfb2ZfbGF0ZV93aW5kb3csIEVuZF9vZl9sYXRlX3dpbmRvdyksIHk9YygwLCAzMCwgMzAsIDApLCBjb2w9YWRqdXN0Y29sb3IoImZpcmVicmljayIsIGFscGhhPSAwLjIpLCBib3JkZXI9YWRqdXN0Y29sb3IoImZpcmVicmljayIsIGFscGhhPSAwLjkpKQoKaGlzdChzcGVjaWZpY19kYXRlc1ssaV0sIGJyZWFrcyA9IDIyLCB4bGltPWMoMTUsMCksIHhsYWI9IksgeWVhcnMgYWdvIiwgY29sPWFkanVzdGNvbG9yKCJjb3JuZmxvd2VyYmx1ZSIsIGFscGhhPSAwLjIpLCBib3JkZXI9YWRqdXN0Y29sb3IoImNvcm5mbG93ZXJibHVlIiwgYWxwaGE9IDAuOSksIG1haW49IiIsIGFkZD1UUlVFKQp9CmBgYAoKCkknbSBjcmVhdGluZyBuZXcgcm93cyBmb3IgdGhpcyB0YWJsZSwgY29tYmluaW5nIGRhdGVzIGluIGRpZmZlcmVudCB3YXlzIHRvIG1ha2UgdGhlIENERnMgYmVsb3cgbG9vayBtb3JlIGF1dGhlbnRpYy4gVGhpcyBtYWtlcyBpdCBzbyB0aGF0IHByZS1hZyBhbHdheXMgaGFwcGVucyBiZWZvcmUgcG9zdC1hZy4gV2hhdCBJJ3ZlIGRvbmUgaXMgZ2l2ZW4gdGhlIGxhdGVyIGRhdGUgdG8gdGhlIGVhcmxpZXIgZGF0ZSB3aGVuIHRob3NlIGRhdGVzIGFyZSBtaXNzaW5nLiAKYGBge3J9CmggPC0gd2hpY2goaXMubmEoZG9tZXN0aWNhdGlvbl90aW1lc1ssM10pKQpkb21lc3RpY2F0aW9uX3RpbWVzIDwtIGNiaW5kKGRvbWVzdGljYXRpb25fdGltZXMsIHJlcChOQSwgbGVuZ3RoKGRvbWVzdGljYXRpb25fdGltZXNbLDFdKSkpCmRvbWVzdGljYXRpb25fdGltZXNbLDldIDwtIGRvbWVzdGljYXRpb25fdGltZXNbLDNdCmRvbWVzdGljYXRpb25fdGltZXNbaCw5XSA8LSBkb21lc3RpY2F0aW9uX3RpbWVzW2gsN10KY29sbmFtZXMoZG9tZXN0aWNhdGlvbl90aW1lcylbOV0gPC0gImFkb3B0IGV4cGxvaXRhdGlvbiBkYXRlIgpkb21lc3RpY2F0aW9uX3RpbWVzWywxMF0gPC0gZG9tZXN0aWNhdGlvbl90aW1lc1ssN10KZG9tZXN0aWNhdGlvbl90aW1lc1t3aGljaChpcy5uYShkb21lc3RpY2F0aW9uX3RpbWVzWywxMF0pKSwxMF0gPC0gMApjb2xuYW1lcyhkb21lc3RpY2F0aW9uX3RpbWVzKVsxMF0gPC0gInN0YXJ0IG9mIGFnIgojc2F2ZShkb21lc3RpY2F0aW9uX3RpbWVzLCBmaWxlPSJ+L0Rlc2t0b3AvSHVtYW4gZGVuc2l0eSBhbmQgdGhlIG9yaWdpbnMgb2YgYWdyaWN1bHR1cmUvRG9tZXN0aWNhdGlvbiB0aW1pbmcgbGFyc29uIDIwMTQuUmRhdGEiKQpgYGAKCgoKSSB0aGluayB0aGVzZSBhcmUgYmVzdCBkZXNjcmliZWQgYnkgYSBjdW1tdWxhdGl2ZSBkaXN0cmlidXRpb24sIHNob3dpbmcgaG93IHRoZXkgYWNjdW11bGF0ZSBvdmVyIHRpbWUuIAoKYGBge3J9CmZvcihpIGluIDE6OCl7CnR5cGVfbnVtYmVyIDwtIGkKCW1hdGNoIDwtIGRvbWVzdGljYXRpb25fdGltZXNbIHdoaWNoKGRvbWVzdGljYXRpb25fdGltZXMkUmVnaW9uID09IGxldmVscyhkb21lc3RpY2F0aW9uX3RpbWVzJFJlZ2lvbilbIHR5cGVfbnVtYmVyXSksIDldCgltYXhlciA8LSBtYXgobWF0Y2gsIG5hLnJtPVRSVUUpCglqIDwtIGVjZGYobWF4ZXItbWF0Y2gpCglwcmludChsZXZlbHMoZG9tZXN0aWNhdGlvbl90aW1lcyRSZWdpb24pWyB0eXBlX251bWJlcl0pCglwcmludChtYXRjaCkKCXByaW50KGopCn0KYGBgCgoKCmBgYHtyfQpwYXIobWZjb2w9YygyLDUpLCBtYXI9Yyg0LDAsNSwwKSkKCnBsb3QoMCwwLCB0eXBlPSJuIiwgeGF4dD0ibiIsIHhsYWI9IiIsIGJ0eT0ibiIpCm10ZXh0KCJQZXJjZW50IG9mIHNwZWNpZXMgdGhhdCB3aWxsIGV2ZW50dWFsbHkgXG4gYmUgZG9tZXN0aWNhdGVkIGluIGEgcmVnaW9uIiwgMiwgbGluZT0tNSwgY2V4PTAuNSkKcGxvdCgwLDAsIHR5cGU9Im4iLCB4YXh0PSJuIiwgeGxhYj0iIiwgYnR5PSJuIikKbXRleHQoIlBlcmNlbnQgb2Ygc3BlY2llcyB0aGF0IHdpbGwgZXZlbnR1YWxseSBcbiBiZSBkb21lc3RpY2F0ZWQgaW4gYSByZWdpb24iLCAyLCBsaW5lPS01LCBjZXg9MC41KQoKZm9yKGkgaW4gMTo4KXsKdHlwZV9udW1iZXIgPC0gaQoJbWF0Y2ggPC0gZG9tZXN0aWNhdGlvbl90aW1lc1sgd2hpY2goZG9tZXN0aWNhdGlvbl90aW1lcyRSZWdpb24gPT0gbGV2ZWxzKGRvbWVzdGljYXRpb25fdGltZXMkUmVnaW9uKVsgdHlwZV9udW1iZXJdKSwgOV0KCW1heGVyIDwtIG1heChtYXRjaCwgbmEucm09VFJVRSkKCWogPC0gZWNkZihtYXhlci1tYXRjaCkKCSNwcmludChqKQoJCnBsb3QoMCwwLCB4bGltPWMoMTUsMCksIHlsaW09YygwLDEwMCksIHlsYWI9IlBlcmNlbnQgb2Ygc3BlY2llcyB0aGF0IHdpbGwgZXZlbnR1YWxseSBcbiBiZSBkb21lc3RpY2F0ZWQgaW4gYSByZWdpb24iLCB4bGFiPSJUaG91c2FuZCB5ZWFycyBhZ28iLCBtYWluPWxldmVscyhkb21lc3RpY2F0aW9uX3RpbWVzJFJlZ2lvbilbIHR5cGVfbnVtYmVyXSwgdHlwZT0ibiIsIHlheHQ9Im4iKQoKeF9zZXEgPC0gcmV2KGMoMCxzZXEoMCwgbWF4ZXIsIGxlbmd0aC5vdXQ9MTAwKSkpCnlfc2VxIDwtIDEwMCAqIChjKDAsIGooc2VxKDAsIG1heGVyLCBsZW5ndGgub3V0PTEwMCkpKSkKCmxpbmVzKHhfc2VxLCB5X3NlcSwgIHlsaW09YygtMSwxKSkKcG9seWdvbihjKDAsIHhfc2VxKSwgYygwLCB5X3NlcSksIGJvcmRlcj1hZGp1c3Rjb2xvcigiY29ybmZsb3dlcmJsdWUiLGFscGhhPTEpLCBjb2w9YWRqdXN0Y29sb3IoImNvcm5mbG93ZXJibHVlIiwgYWxwaGE9MC4yKSkKaWYoaSA9PSAyIHwgaSA9PSAxKWF4aXMoMikKCmlmKGkgPT0gMyltdGV4dCgiQ3VtbXVsYXRpdmUgZGlzdHJpYnV0aW9uIGZ1bmN0aW9uIGZvciB0aGUgYWNjdW11bGF0aW9uIG9mIGRvbWVzdGljYXRlcyIsIDMsIGxpbmU9My44LCBjb2w9ImNvcm5mbG93ZXJibHVlIikKfQoKCmBgYAoKCmBgYHtyfQpwYXIobWZjb2w9YygyLDUpLCBtYXI9Yyg0LDAsNSwwKSkKCnBsb3QoMCwwLCB0eXBlPSJuIiwgeGF4dD0ibiIsIHhsYWI9IiIsIGJ0eT0ibiIpCm10ZXh0KCJQZXJjZW50IG9mIHNwZWNpZXMgdGhhdCB3aWxsIGV2ZW50dWFsbHkgXG4gYmUgZG9tZXN0aWNhdGVkIGluIGEgcmVnaW9uIiwgMiwgbGluZT0tNSwgY2V4PTAuNSkKCnBsb3QoMCwwLCB0eXBlPSJuIiwgeGF4dD0ibiIsIHhsYWI9IiIsIGJ0eT0ibiIpCm10ZXh0KCJQZXJjZW50IG9mIHNwZWNpZXMgdGhhdCB3aWxsIGV2ZW50dWFsbHkgXG4gYmUgZG9tZXN0aWNhdGVkIGluIGEgcmVnaW9uIiwgMiwgbGluZT0tNSwgY2V4PTAuNSkKCmZvcihpIGluIDE6OCl7CnR5cGVfbnVtYmVyIDwtIGkKCW1hdGNoIDwtIGRvbWVzdGljYXRpb25fdGltZXNbIHdoaWNoKGRvbWVzdGljYXRpb25fdGltZXMkUmVnaW9uID09IGxldmVscyhkb21lc3RpY2F0aW9uX3RpbWVzJFJlZ2lvbilbIHR5cGVfbnVtYmVyXSksIDldCgltYXhlciA8LSBtYXgobWF0Y2gsIG5hLnJtPVRSVUUpCglqIDwtIGVjZGYobWF4ZXItbWF0Y2gpCgkjcHJpbnQoaikKCQpwbG90KDAsMCwgeGxpbT1jKDE1LDApLCB5bGltPWMoMCwxMDApLCB5bGFiPSJQZXJjZW50IG9mIHNwZWNpZXMgdGhhdCB3aWxsIGV2ZW50dWFsbHkgXG4gYmUgZG9tZXN0aWNhdGVkIGluIGEgcmVnaW9uIiwgeGxhYj0iVGhvdXNhbmQgeWVhcnMgYWdvIiwgbWFpbj1sZXZlbHMoZG9tZXN0aWNhdGlvbl90aW1lcyRSZWdpb24pWyB0eXBlX251bWJlcl0sIHR5cGU9Im4iLCB5YXh0PSJuIikKCnhfc2VxIDwtIHJldihjKDAsc2VxKDAsIG1heGVyLCBsZW5ndGgub3V0PTEwMCkpKQp5X3NlcSA8LSAxMDAgKiAoYygwLCBqKHNlcSgwLCBtYXhlciwgbGVuZ3RoLm91dD0xMDApKSkpCgpsaW5lcyh4X3NlcSwgeV9zZXEsICB5bGltPWMoLTEsMSkpCnBvbHlnb24oYygwLCB4X3NlcSksIGMoMCwgeV9zZXEpLCBib3JkZXI9YWRqdXN0Y29sb3IoImNvcm5mbG93ZXJibHVlIixhbHBoYT0xKSwgY29sPWFkanVzdGNvbG9yKCJjb3JuZmxvd2VyYmx1ZSIsIGFscGhhPTAuMikpCmFibGluZSh2PSBtYXhlciAtIHF1YW50aWxlKGopWzJdLCBjb2w9ImxpbWVncmVlbiIsIGx3ZD0yKQppZihpID09IDIgfCBpID09IDEpYXhpcygyKQppZihpID09IDIpbXRleHQoIjI1JSIsIDMsIGxpbmU9My41LCBhZGo9LTEsIGNvbD0ibGltZWdyZWVuIikKaWYoaSA9PSAzKW10ZXh0KCJDdW1tdWxhdGl2ZSBkaXN0cmlidXRpb24gZnVuY3Rpb24gZm9yIHRoZSBhY2N1bXVsYXRpb24gb2YgZG9tZXN0aWNhdGVzIiwgMywgbGluZT0zLjgsIGNvbD0iY29ybmZsb3dlcmJsdWUiKQppZihpID09IDQpbXRleHQoIkNob29zZSBhIHkgdG8gcHJlZGljdCBhbiB4IiwgMywgbGluZT0zLjMsIGNvbD0iY29ybmZsb3dlcmJsdWUiKQoJYnJlYWtfb25lIDwtIG1heGVyCgkJCWJyZWFrX3R3byA8LSBtYXhlciAtIHF1YW50aWxlKGopWzJdCgkJCQkKCXBvbHlnb24oeD1jKGJyZWFrX3R3bywgYnJlYWtfdHdvLCBicmVha19vbmUsIGJyZWFrX29uZSksIHk9YygwLCAxLCAxLCAwKSwgY29sPWFkanVzdGNvbG9yKCJjb3JuZmxvd2VyYmx1ZSIsIGFscGhhPTAuMiksIGJvcmRlcj1hZGp1c3Rjb2xvcigiY29ybmZsb3dlcmJsdWUiLGFscGhhPTEpKQoJCQlsaW5lcyh4PWMoYnJlYWtfdHdvLCBicmVha190d28pLCB5PWMoMCwtMSksIGNvbD0iY29ybmZsb3dlcmJsdWUiKQoJCQlhYmxpbmUoaCA9IDI1LCBjb2w9ImxpbWVncmVlbiIsIGx3ZD0yKQp9CgoKYGBgCk1ha2UgdGhpcyBhIGZ1bmN0aW9uLiAKVGhlcmUgaXMgYSBjaG9pY2Ugb2YgdHdvIG1ldGhvZHMgaGVyZS4gQXQgdGhlIGVuZCBvZiB0aGlzIHNlY3Rpb24gd2UgbmVlZCB0byBwcmludCB0aGUgZGVzaXNpb24gd2UncmUgcGFzc2luZyB0byB0aGUgbGF0ZXIgYW5hbHlzZXMuIAoKCgoKCgojIyBEZWZpbmluZyBvcmlnaW5zCiFbXShMYXJzb25fb3JpZ2lucy5qcGcpCgoKYGBge3J9Cm9yaWdpbnMgPC0gcmVhZFNoYXBlUG9seSgnT3JpZ2luc191cGRhdGVkLnNocCcpCnByb2o0c3RyaW5nKG9yaWdpbnMpIDwtIENSUygiK3Byb2o9bG9uZ2xhdCArZGF0dW09V0dTODQiKQojcHJvaiA8LSBDUlMoIitwcm9qPWVxYyArbGF0X3RzPTAgK2xhdF8wPTAgK2xvbl8wPTAgK3hfMD0wICt5XzA9MCArZWxscHM9V0dTODQgK3VuaXRzPW0gK25vX2RlZnMiKQojb3JpZ2lucy5lYSA8LSBzcFRyYW5zZm9ybShvcmlnaW5zLCBwcm9qKQoKCiNzdWJzZXRfb3JkZXIgPC0gYygxLCAyLCAzLCA1LCA2LCA4LCA5LCAxMCwgMTEsIDEyLCAxNywgMTgpCnN1YnNldF9vcmRlciA8LSBjKDgsIDEwLCA5LCA1LCAxOCwgNywgNiwgMjAsIDEsIDIsIDEzLCAxNCkKb3JpZ2luc19zdWJzZXQgPC0gb3JpZ2luc1tzdWJzZXRfb3JkZXIsXQpvcmlnaW5zX3N1YnNldCRDT05USU5FTlQKb3JpZ2luc19zdWJzZXQkbmFtZQoKCmBgYAoKYGBge3J9CgptYXAoKQptYXAob3JpZ2lucywgYWRkPVRSVUUsIGZpbGw9VFJVRSwgY29sPWFkanVzdGNvbG9yKCJjb3JuZmxvd2VyYmx1ZSIsIGFscGhhPTEpKQoKYGBgCgogCgojI21hcCBmb3IgZmluYWwgZmlndXJlCgoKCgoKIyMjIyBNYWtlIHRoZSBtYXAgZm9yIHRoZSBjZW50ZXIgcGFuZWwgKCM1IG9uIGxheW91dCBwYW5lbCkKCmBgYHtyfQpkIDwtIHJlYWRQTkcoImVhcnRoLnBuZyIpCmBgYAoKIVtdKGVhcnRoLnBuZykKCgoKCmBgYHtyfQpwbmcoZmlsZT1wYXN0ZSgiNDA5NjEucG5nIixzZXA9IiIpLHdpZHRoPTE0NDAsaGVpZ2h0PTcyMCwgYmc9InRyYW5zcGFyZW50IikKcGFyKG1hcj1jKDAsMCwwLDApLCBtYWk9YygwLDAsMCwwKSkKcGxvdChzZXEoLTE4MCwgMTgwLCBsZW5ndGgub3V0ID0gMTkpLCBzZXEoLTkwLCA5MCwgbGVuZ3RoLm91dCA9IDE5KSwgdHlwZT0ibiIseGxpbT1jKC0xODAsIDE4MCkseWxpbT1jKC05MCwgOTApLCB4YXh0PSJuIikKeF9hZGogPC0gMAp5X2FkaiA8LSAwCnJhc3RlckltYWdlKGQsIC0xODAgLSB4X2FkaiwgLTkwIC0geV9hZGosIDE4MCArIHhfYWRqLCA5MCArIHlfYWRqLCBpbnRlcnBvbGF0ZT1UUlVFLCBjb2w9ZCkKCiNwb2x5Z29uKHg9YygtMTgwLC0xODAsIDE4MCwxODApLCB5PWMoLTkwLCA5MCwgOTAsIC05MCksIGNvbD1hZGp1c3Rjb2xvcigid2hpdGUiLCBhbHBoYT0wLjEpKQojcmFzdGVySW1hZ2UoZCwgLTEzLjUsIC0xMy41LCAzNzUsIDM3NSwgaW50ZXJwb2xhdGU9VFJVRSwgY29sPWQpCnBsb3Qob3JpZ2luc19zdWJzZXQsIGFkZD1UUlVFLCBjb2w9YWRqdXN0Y29sb3IoIndoaXRlIiwgYWxwaGE9LjgpLCB4YXh0PSJuIiwgYm9yZGVyPSJ3aGl0ZSIsIGx3ZD00KSAjc3RpbGwgbmVlZCB0byByZXByb2plY3QhISEKTFcgPC0gNApsaW5lcyh4ID0gYygtMTAwICwgLTEzMCksIHkgPSBjKDIwICwgOTApLCBsdHk9MiwgY29sPWFkanVzdGNvbG9yKCJ3aGl0ZSIsIGFscGhhPTEpLCBsd2Q9TFcpICNNZXNvYW1lcmljYQpsaW5lcyh4ID0gYygtODAgLCAtNzApLCB5ID0gYygwICwgOTApLCBsdHk9MiwgY29sPWFkanVzdGNvbG9yKCJ3aGl0ZSIsIGFscGhhPTEpLCBsd2Q9TFcpICNOV19Mb3dsYW5kX1NBCmxpbmVzKHggPSBjKC03NCAsIC01ICksIHkgPSBjKDUgLCA5MCksIGx0eT0yLCBjb2w9YWRqdXN0Y29sb3IoIndoaXRlIiwgYWxwaGE9MSksIGx3ZD1MVykgI05fTG93bGFuZF9TQQpsaW5lcyh4ID0gYyg0MCAsIDMwKSwgeSA9IGMoMzYgLCA5MCksIGx0eT0yLCBjb2w9YWRqdXN0Y29sb3IoIndoaXRlIiwgYWxwaGE9MSksIGx3ZD1MVykgI0ZlcnRpbGVfQ3Jlc2MKbGluZXMoeCA9IGMoMTEwICwgNzApLCB5ID0gYyg0MCAsIDkwKSwgbHR5PTIsIGNvbD1hZGp1c3Rjb2xvcigid2hpdGUiLCBhbHBoYT0xKSwgbHdkPUxXKSAjQ2hpbmVzZV9sb2VzcwpsaW5lcyh4ID0gYygxNDIgLCAxNjApLCB5ID0gYygtNSAsIDkwKSwgbHR5PTIsIGNvbD1hZGp1c3Rjb2xvcigid2hpdGUiLCBhbHBoYT0xKSwgbHdkPUxXKSAjTmV3X0d1aW5lYQpsaW5lcyh4ID0gYygtODUgLCAtMTMwKSwgeSA9IGMoMzMgLCAtOTApLCBsdHk9MiwgY29sPWFkanVzdGNvbG9yKCJ3aGl0ZSIsIGFscGhhPTEpLCBsd2Q9TFcpICNFX05vcnRoX0FtZXJpCmxpbmVzKHggPSBjKC02OCAsIC03NSksIHkgPSBjKC0yNSAsIC05MCksIGx0eT0yLCBjb2w9YWRqdXN0Y29sb3IoIndoaXRlIiwgYWxwaGE9MSksIGx3ZD1MVykgI0MvU19BbmRlcwpsaW5lcyh4ID0gYygtMTAgLCAtMjApLCB5ID0gYyggMTUsIC05MCksIGx0eT0yLCBjb2w9YWRqdXN0Y29sb3IoIndoaXRlIiwgYWxwaGE9MSksIGx3ZD1MVykgI1dfQWZyaWNhbl9TYXYKbGluZXMoeCA9IGMoMjUgLCA0MCksIHkgPSBjKDkgLCAtOTApLCBsdHk9MiwgY29sPWFkanVzdGNvbG9yKCJ3aGl0ZSIsIGFscGhhPTEpLCBsd2Q9TFcpICNTdWRhbmljX1NhdmFuCmxpbmVzKHggPSBjKDg3ICwgMTAwKSwgeSA9IGMoMjAgLCAtOTApLCBsdHk9MiwgY29sPWFkanVzdGNvbG9yKCJ3aGl0ZSIsIGFscGhhPTEpLCBsd2Q9TFcpICNHYW5nZXNfRV9JbmRpCmxpbmVzKHggPSBjKDEyMCAsIDE2MCksIHkgPSBjKDMwICwgLTkwKSwgbHR5PTIsIGNvbD1hZGp1c3Rjb2xvcigid2hpdGUiLCBhbHBoYT0xKSwgbHdkPUxXKSAjTG93ZXItTWlkZGxlWQoKCgpkZXYub2ZmKCkKYGBgCgpgYGB7cn0KI25lZWQgdG8gb3ZlcnBsb3Qgc28gaXQgZmlsbHMgdGhlIHdob2xlIGZyYW1lLiB0aGUgZGVmYXVsdCBwbG90IGxlYXZlcyBhIGJvcmRlciBhcm91bmQgdGhlIHBob3RvCmQgPC0gcmVhZFBORygiNDA5NjEucG5nIikKcG5nKGZpbGU9cGFzdGUoIjQwOTYyLnBuZyIsc2VwPSIiKSx3aWR0aD0xNDQwLGhlaWdodD03MjAsIGJnPSJ0cmFuc3BhcmVudCIpCnBhcihtYXI9YygwLDAsMCwwKSwgbWFpPWMoMCwwLDAsMCkpCnBsb3Qoc2VxKC0xODAsIDE4MCwgbGVuZ3RoLm91dCA9IDE5KSwgc2VxKC05MCwgOTAsIGxlbmd0aC5vdXQgPSAxOSksIHR5cGU9Im4iLHhsaW09YygtMTgwLCAxODApLHlsaW09YygtOTAsIDkwKSwgeGF4dD0ibiIpCnhfYWRqIDwtIDMwCnlfYWRqIDwtIDE1CnJhc3RlckltYWdlKGQsIC0xODAgLSB4X2FkaiwgLTkwIC0geV9hZGosIDE4MCArIHhfYWRqLCA5MCArIHlfYWRqLCBpbnRlcnBvbGF0ZT1UUlVFLCBjb2w9ZCkKCmRldi5vZmYoKQpgYGAKCgoKCiFbXSg0MDk2Mi5wbmcpCgoKCiMjSW1wb3J0IHJhc3RlciBkYXRhIAoKdW5pdCA9IHBvdGVudGlhbCBodW1hbiBkZW5zaXR5IGFuZCBwcm9qZWN0ZWQgcHJvZHVjdGl2aXR5CmBgYHtyfQojc3Vic2V0IGFuZCByZW9yZGVyIG9yaWdpbnMuIFRoaXMgaXMgY3VycmVudGx5IGRvbmUgYXQgdGhlIGVuZCBvZiB0aGUgcGxvdCBidXQgc2hvdWxkIGJlIG1vdmVkIGZvcndhcmQuCgojIExvYWQgZGF0YSBmb3IgcG9wdWxhdGlvbiBkZW5zaXR5CmxvYWQoIlBvcERfYWxsX0RlY2VtYmVyLnJkYXRhIikKUG9wRC5BTEwKYGBgCgpgYGB7cn0KIyBFeHRyYWN0IGRhdGEgdG8gYSBtYXRyaXgKUG9wIDwtIHZhbHVlcyhQb3BELkFMTCkKciA8LSByYXN0ZXIoUG9wRC5BTEwsIDEpCnIKYGBgCgoKCgojI0dBTSB2cyBMb2VzcyBzbW9vdGhpbmcgbW9kZWxzCiMjIyBKdXN0aWZpY2F0aW9uIGZvciBzbW9vdGhpbmcgTW9kZWxzLgogIFdlIG5lZWQgdG8ganVzdGlmeSBvdXIgZGVjaXNpb24gdG8gdXNlIGEgR0FNIG92ZXIgb3RoZXIgbW9kZWxzLiBUaGlzIHNob3VsZCBpbmNsdWRlIGNpdGF0aW9ucyB0byBiYWNrIHVwIHRob3NlIGFyZ3VtZW50cy4KICAKICBUaGlzIGlzIGEgZGF0YSBmaXR0aW5nIG1vZGVsLCBub3QgYSB0aGVvcnkgZHJpdmVuIG1vZGVsLiBUaGVyZSBpcyBubyBwcm9jZXNzIG9yIG1lY2hhbmlzbSBidWlsdCBpbnRvIHRoZSBtb2RlbCwgdGhlIG1vZGVsIGlzIGp1c3QgZmxleGFibHkgZml0dGluZyB0aGUgZGF0YS4gVGhlcmUgYXJlIG1hbnkgd2F5cyBvZiBkb2luZyB0aGlzLCBidXQgaGVyZSB3ZSdyZSBjb21wZXRpbmcgdGhlIEdBTSBhbmQgTG9lc3MgYWdhaW5zdCBlYWNob3RoZXIgdG8gdGVsbCB1cyBhIGJldHRlciBzdG9yeS4gVGhlIG1vZGVscyBhcmUgdmVyeSBzaW1pbGFyLCBidXQgc2xpZ2h0bHkgZGlmZmVyZW50LiBUaGUgTG9lc3MgbW9kZWwgaXMgYSBsb2NhbCBhdmVyYWdpbmcgbW9kZWwgd2hlcmUgdGhlIGZsZXhpYmxlIHBhcmFtZXRlciBpcyBob3cgbWFueSBvZiB0aGUgc3Vycm91bmRpbmcgcG9pbnRzIHRvIHRha2UgaW50byBjb25zaWRlcmF0aW9uLCBhbmQgdGhlIEdBTSBpcyBhIGdsb2JhbCBhdmVyYWdpbmcgbW9kZWwgd2hlcmUgdGhlIGZsZXhpYmxlIHBhcmFtZXRlciBpcyB0aGUgbnVtYmVyIG9mIGJlbmRzIGluIHRoZSBsaW5lIGFuZCB0aGVuIHRob3NlIGJlbmRzIGFyZSBmaXQgaW4gdGhlIGJlc3Qgd2F5IHBvc3NpYmxlLiBUaGUgYWR2YW50YWdlIG9mIEdBTSBpcyB0aGF0IGl0IHByb2R1Y2VzIGEgdHJhbnNsYXRhYmxlIG1vZGVsIHRoYXQgY291bGQgYmUgZml0IHRvIGRpZmZlcmVudCBkYXRhc2V0cy4gVGhlIGRpc2FkdmFudGFnZSBvZiBHQU0gaXMgdGhhdCBpdCBvZnRlbiBwcm9kdWNlcyBwb29yIGRhdGEgZml0cy4gVGhlIExvZXNzIG1vZGVsIG1vdmVzIGFsb25nIHRoZSB4LWF4ZXMgYW5kIGF2ZXJhZ2VzIGFsb25nIHRoZSB3YXkuIFRoZSBhZHZhbnRhZ2Ugb2YgYSBsb2VzcyBpcyB0aGF0IGl0IHRlbmRzIHRvIHRyYWNrIHRoZSBkYXRhIG5pY2VseSB3aXRoIGZldyBwYXJhbWV0ZXJzLCBidXQgaXQgZG9lc24ndCBwcm9kdWNlIGEgdHJhbnNwb3J0YWJsZSBtb2RlbCBmb3IgY29tcGFyaW5nIGFnYWluc3Qgb3RoZXIgZGF0YXNldHMuIAogIAojIyMjIGFib3V0IExPRVNTIGZyb20gdGhlIGludGVybmV0OiBodHRwOi8vd3d3Lml0bC5uaXN0Lmdvdi9kaXY4OTgvaGFuZGJvb2svcG1kL3NlY3Rpb24xL3BtZDE0NC5odG0KCiJMT0VTUywgb3JpZ2luYWxseSBwcm9wb3NlZCBieSBDbGV2ZWxhbmQgKDE5NzkpIGFuZCBmdXJ0aGVyIGRldmVsb3BlZCBieSBDbGV2ZWxhbmQgYW5kIERldmxpbiAoMTk4OCksIHNwZWNpZmljYWxseSBkZW5vdGVzIGEgbWV0aG9kIHRoYXQgaXMgKHNvbWV3aGF0KSBtb3JlIGRlc2NyaXB0aXZlbHkga25vd24gYXMgbG9jYWxseSB3ZWlnaHRlZCBwb2x5bm9taWFsIHJlZ3Jlc3Npb24uIEF0IGVhY2ggcG9pbnQgaW4gdGhlIGRhdGEgc2V0IGEgbG93LWRlZ3JlZSBwb2x5bm9taWFsIGlzIGZpdCB0byBhIHN1YnNldCBvZiB0aGUgZGF0YSwgd2l0aCBleHBsYW5hdG9yeSB2YXJpYWJsZSB2YWx1ZXMgbmVhciB0aGUgcG9pbnQgd2hvc2UgcmVzcG9uc2UgaXMgYmVpbmcgZXN0aW1hdGVkLiBUaGUgcG9seW5vbWlhbCBpcyBmaXQgdXNpbmcgd2VpZ2h0ZWQgbGVhc3Qgc3F1YXJlcywgZ2l2aW5nIG1vcmUgd2VpZ2h0IHRvIHBvaW50cyBuZWFyIHRoZSBwb2ludCB3aG9zZSByZXNwb25zZSBpcyBiZWluZyBlc3RpbWF0ZWQgYW5kIGxlc3Mgd2VpZ2h0IHRvIHBvaW50cyBmdXJ0aGVyIGF3YXkuIFRoZSB2YWx1ZSBvZiB0aGUgcmVncmVzc2lvbiBmdW5jdGlvbiBmb3IgdGhlIHBvaW50IGlzIHRoZW4gb2J0YWluZWQgYnkgZXZhbHVhdGluZyB0aGUgbG9jYWwgcG9seW5vbWlhbCB1c2luZyB0aGUgZXhwbGFuYXRvcnkgdmFyaWFibGUgdmFsdWVzIGZvciB0aGF0IGRhdGEgcG9pbnQuIFRoZSBMT0VTUyBmaXQgaXMgY29tcGxldGUgYWZ0ZXIgcmVncmVzc2lvbiBmdW5jdGlvbiB2YWx1ZXMgaGF2ZSBiZWVuIGNvbXB1dGVkIGZvciBlYWNoIG9mIHRoZSBuIGRhdGEgcG9pbnRzLiBNYW55IG9mIHRoZSBkZXRhaWxzIG9mIHRoaXMgbWV0aG9kLCBzdWNoIGFzIHRoZSBkZWdyZWUgb2YgdGhlIHBvbHlub21pYWwgbW9kZWwgYW5kIHRoZSB3ZWlnaHRzLCBhcmUgZmxleGlibGUuIAogIFRoZSBsb2NhbCBwb2x5bm9taWFscyBmaXQgdG8gZWFjaCBzdWJzZXQgb2YgdGhlIGRhdGEgYXJlIGFsbW9zdCBhbHdheXMgb2YgZmlyc3Qgb3Igc2Vjb25kIGRlZ3JlZTsgdGhhdCBpcywgZWl0aGVyIGxvY2FsbHkgbGluZWFyIChpbiB0aGUgc3RyYWlnaHQgbGluZSBzZW5zZSkgb3IgbG9jYWxseSBxdWFkcmF0aWMuIFVzaW5nIGEgemVybyBkZWdyZWUgcG9seW5vbWlhbCB0dXJucyBMT0VTUyBpbnRvIGEgd2VpZ2h0ZWQgbW92aW5nIGF2ZXJhZ2UuIFN1Y2ggYSBzaW1wbGUgbG9jYWwgbW9kZWwgbWlnaHQgd29yayB3ZWxsIGZvciBzb21lIHNpdHVhdGlvbnMsIGJ1dCBtYXkgbm90IGFsd2F5cyBhcHByb3hpbWF0ZSB0aGUgdW5kZXJseWluZyBmdW5jdGlvbiB3ZWxsIGVub3VnaC4gSGlnaGVyLWRlZ3JlZSBwb2x5bm9taWFscyB3b3VsZCB3b3JrIGluIHRoZW9yeSwgYnV0IHlpZWxkIG1vZGVscyB0aGF0IGFyZSBub3QgcmVhbGx5IGluIHRoZSBzcGlyaXQgb2YgTE9FU1MuIExPRVNTIGlzIGJhc2VkIG9uIHRoZSBpZGVhcyB0aGF0IGFueSBmdW5jdGlvbiBjYW4gYmUgd2VsbCBhcHByb3hpbWF0ZWQgaW4gYSBzbWFsbCBuZWlnaGJvcmhvb2QgYnkgYSBsb3ctb3JkZXIgcG9seW5vbWlhbCBhbmQgdGhhdCBzaW1wbGUgbW9kZWxzIGNhbiBiZSBmaXQgdG8gZGF0YSBlYXNpbHkuIEhpZ2gtZGVncmVlIHBvbHlub21pYWxzIHdvdWxkIHRlbmQgdG8gb3ZlcmZpdCB0aGUgZGF0YSBpbiBlYWNoIHN1YnNldCBhbmQgYXJlIG51bWVyaWNhbGx5IHVuc3RhYmxlLCBtYWtpbmcgYWNjdXJhdGUgY29tcHV0YXRpb25zIGRpZmZpY3VsdC4KICBUaGUgYmlnZ2VzdCBhZHZhbnRhZ2UgTE9FU1MgaGFzIG92ZXIgbWFueSBvdGhlciBtZXRob2RzIGlzIHRoZSBmYWN0IHRoYXQgaXQgZG9lcyBub3QgcmVxdWlyZSB0aGUgc3BlY2lmaWNhdGlvbiBvZiBhIGZ1bmN0aW9uIHRvIGZpdCBhIG1vZGVsIHRvIGFsbCBvZiB0aGUgZGF0YSBpbiB0aGUgc2FtcGxlLiBJbnN0ZWFkIHRoZSBhbmFseXN0IG9ubHkgaGFzIHRvIHByb3ZpZGUgYSBzbW9vdGhpbmcgcGFyYW1ldGVyIHZhbHVlIGFuZCB0aGUgZGVncmVlIG9mIHRoZSBsb2NhbCBwb2x5bm9taWFsLiBJbiBhZGRpdGlvbiwgTE9FU1MgaXMgdmVyeSBmbGV4aWJsZSwgbWFraW5nIGl0IGlkZWFsIGZvciBtb2RlbGluZyBjb21wbGV4IHByb2Nlc3NlcyBmb3Igd2hpY2ggbm8gdGhlb3JldGljYWwgbW9kZWxzIGV4aXN0LiBUaGVzZSB0d28gYWR2YW50YWdlcywgY29tYmluZWQgd2l0aCB0aGUgc2ltcGxpY2l0eSBvZiB0aGUgbWV0aG9kLCBtYWtlIExPRVNTIG9uZSBvZiB0aGUgbW9zdCBhdHRyYWN0aXZlIG9mIHRoZSBtb2Rlcm4gcmVncmVzc2lvbiBtZXRob2RzIGZvciBhcHBsaWNhdGlvbnMgdGhhdCBmaXQgdGhlIGdlbmVyYWwgZnJhbWV3b3JrIG9mIGxlYXN0IHNxdWFyZXMgcmVncmVzc2lvbiBidXQgd2hpY2ggaGF2ZSBhIGNvbXBsZXggZGV0ZXJtaW5pc3RpYyBzdHJ1Y3R1cmUuCiAgQWx0aG91Z2ggTE9FU1MgZG9lcyBzaGFyZSBtYW55IG9mIHRoZSBiZXN0IGZlYXR1cmVzIG9mIG90aGVyIGxlYXN0IHNxdWFyZXMgbWV0aG9kcywgZWZmaWNpZW50IHVzZSBvZiBkYXRhIGlzIG9uZSBhZHZhbnRhZ2UgdGhhdCBMT0VTUyBkb2Vzbid0IHNoYXJlLiBMT0VTUyByZXF1aXJlcyBmYWlybHkgbGFyZ2UsIGRlbnNlbHkgc2FtcGxlZCBkYXRhIHNldHMgaW4gb3JkZXIgdG8gcHJvZHVjZSBnb29kIG1vZGVscy4gVGhpcyBpcyBub3QgcmVhbGx5IHN1cnByaXNpbmcsIGhvd2V2ZXIsIHNpbmNlIExPRVNTIG5lZWRzIGdvb2QgZW1waXJpY2FsIGluZm9ybWF0aW9uIG9uIHRoZSBsb2NhbCBzdHJ1Y3R1cmUgb2YgdGhlIHByb2Nlc3MgaW4gb3JkZXIgcGVyZm9ybSB0aGUgbG9jYWwgZml0dGluZy4gSW4gZmFjdCwgZ2l2ZW4gdGhlIHJlc3VsdHMgaXQgcHJvdmlkZXMsIExPRVNTIGNvdWxkIGFyZ3VhYmx5IGJlIG1vcmUgZWZmaWNpZW50IG92ZXJhbGwgdGhhbiBvdGhlciBtZXRob2RzIGxpa2Ugbm9ubGluZWFyIGxlYXN0IHNxdWFyZXMuIEl0IG1heSBzaW1wbHkgZnJvbnRsb2FkIHRoZSBjb3N0cyBvZiBhbiBleHBlcmltZW50IGluIGRhdGEgY29sbGVjdGlvbiBidXQgdGhlbiByZWR1Y2UgYW5hbHlzaXMgY29zdHMuCkFub3RoZXIgZGlzYWR2YW50YWdlIG9mIExPRVNTIGlzIHRoZSBmYWN0IHRoYXQgaXQgZG9lcyBub3QgcHJvZHVjZSBhIHJlZ3Jlc3Npb24gZnVuY3Rpb24gdGhhdCBpcyBlYXNpbHkgcmVwcmVzZW50ZWQgYnkgYSBtYXRoZW1hdGljYWwgZm9ybXVsYS4gVGhpcyBjYW4gbWFrZSBpdCBkaWZmaWN1bHQgdG8gdHJhbnNmZXIgdGhlIHJlc3VsdHMgb2YgYW4gYW5hbHlzaXMgdG8gb3RoZXIgcGVvcGxlLiBJbiBvcmRlciB0byB0cmFuc2ZlciB0aGUgcmVncmVzc2lvbiBmdW5jdGlvbiB0byBhbm90aGVyIHBlcnNvbiwgdGhleSB3b3VsZCBuZWVkIHRoZSBkYXRhIHNldCBhbmQgc29mdHdhcmUgZm9yIExPRVNTIGNhbGN1bGF0aW9ucy4gSW4gbm9ubGluZWFyIHJlZ3Jlc3Npb24sIG9uIHRoZSBvdGhlciBoYW5kLCBpdCBpcyBvbmx5IG5lY2Vzc2FyeSB0byB3cml0ZSBkb3duIGEgZnVuY3Rpb25hbCBmb3JtIGluIG9yZGVyIHRvIHByb3ZpZGUgZXN0aW1hdGVzIG9mIHRoZSB1bmtub3duIHBhcmFtZXRlcnMgYW5kIHRoZSBlc3RpbWF0ZWQgdW5jZXJ0YWludHkuIERlcGVuZGluZyBvbiB0aGUgYXBwbGljYXRpb24sIHRoaXMgY291bGQgYmUgZWl0aGVyIGEgbWFqb3Igb3IgYSBtaW5vciBkcmF3YmFjayB0byB1c2luZyBMT0VTUy4KRmluYWxseSwgYXMgZGlzY3Vzc2VkIGFib3ZlLCBMT0VTUyBpcyBhIGNvbXB1dGF0aW9uYWwgaW50ZW5zaXZlIG1ldGhvZC4gVGhpcyBpcyBub3QgdXN1YWxseSBhIHByb2JsZW0gaW4gb3VyIGN1cnJlbnQgY29tcHV0aW5nIGVudmlyb25tZW50LCBob3dldmVyLCB1bmxlc3MgdGhlIGRhdGEgc2V0cyBiZWluZyB1c2VkIGFyZSB2ZXJ5IGxhcmdlLiBMT0VTUyBpcyBhbHNvIHByb25lIHRvIHRoZSBlZmZlY3RzIG9mIG91dGxpZXJzIGluIHRoZSBkYXRhIHNldCwgbGlrZSBvdGhlciBsZWFzdCBzcXVhcmVzIG1ldGhvZHMuIFRoZXJlIGlzIGFuIGl0ZXJhdGl2ZSwgcm9idXN0IHZlcnNpb24gb2YgTE9FU1MgW0NsZXZlbGFuZCAoMTk3OSldIHRoYXQgY2FuIGJlIHVzZWQgdG8gcmVkdWNlIExPRVNTJyBzZW5zaXRpdml0eSB0byBvdXRsaWVycywgYnV0IGV4dHJlbWUgb3V0bGllcnMgY2FuIHN0aWxsIG92ZXJjb21lIGV2ZW4gdGhlIHJvYnVzdCBtZXRob2QuCgkkcSQgaXMgY2FsbGVkIHRoZSBzbW9vdGhpbmcgcGFyYW1ldGVyIGJlY2F1c2UgaXQgY29udHJvbHMgdGhlIGZsZXhpYmlsaXR5IG9mIHRoZSBMT0VTUyByZWdyZXNzaW9uIGZ1bmN0aW9uLiBMYXJnZSB2YWx1ZXMgb2YgcSBwcm9kdWNlIHRoZSBzbW9vdGhlc3QgZnVuY3Rpb25zIHRoYXQgd2lnZ2xlIHRoZSBsZWFzdCBpbiByZXNwb25zZSB0byBmbHVjdHVhdGlvbnMgaW4gdGhlIGRhdGEuIFRoZSBzbWFsbGVyICRxJCBpcywgdGhlIGNsb3NlciB0aGUgcmVncmVzc2lvbiBmdW5jdGlvbiB3aWxsIGNvbmZvcm0gdG8gdGhlIGRhdGEuIFVzaW5nIHRvbyBzbWFsbCBhIHZhbHVlIG9mIHRoZSBzbW9vdGhpbmcgcGFyYW1ldGVyIGlzIG5vdCBkZXNpcmFibGUsIGhvd2V2ZXIsIHNpbmNlIHRoZSByZWdyZXNzaW9uIGZ1bmN0aW9uIHdpbGwgZXZlbnR1YWxseSBzdGFydCB0byBjYXB0dXJlIHRoZSByYW5kb20gZXJyb3IgaW4gdGhlIGRhdGEuIFVzZWZ1bCB2YWx1ZXMgb2YgdGhlIHNtb290aGluZyBwYXJhbWV0ZXIgdHlwaWNhbGx5IGxpZSBpbiB0aGUgcmFuZ2UgJDAuMjUkIHRvICQwLjUkIGZvciBtb3N0IExPRVNTIGFwcGxpY2F0aW9ucy4iCiAgCiMjIyMgYWJvdXQgR0FNIGZyb20gdGhlIGludGVybmV0OiBodHRwOi8vbXVsdGl0aHJlYWRlZC5zdGl0Y2hmaXguY29tL2Jsb2cvMjAxNS8wNy8zMC9nYW0vCiAgCiAgIkltYWdpbmUgdGhhdCB5b3Ugc3RlcCBpbnRvIGEgcm9vbSBvZiBkYXRhIHNjaWVudGlzdHM7IHRoZSBkcmVzcyBjb2RlIGlzIGNhc3VhbCBhbmQgdGhlIHNjZW50IG9mIHN0cm9uZyBjb2ZmZWUgaXMgaGFuZ2luZyBpbiB0aGUgYWlyLiBZb3UgYXNrIHRoZSBkYXRhIHNjaWVudGlzdHMgaWYgdGhleSByZWd1bGFybHkgdXNlIGdlbmVyYWxpemVkIGFkZGl0aXZlIG1vZGVscyAoR0FNKSB0byBkbyB0aGVpciB3b3JrLiBWZXJ5IGZldyB3aWxsIHNheSB5ZXMsIGlmIGFueSBhdCBhbGwuCgpOb3cgbGV04oCZcyByZXBsYXkgdGhlIHNjZW5hcmlvLCBvbmx5IHRoaXMgdGltZSB3ZSByZXBsYWNlIEdBTSB3aXRoLCBzYXksIHJhbmRvbSBmb3Jlc3Qgb3Igc3VwcG9ydCB2ZWN0b3IgbWFjaGluZXMgKFNWTSkuIEV2ZXJ5b25lIHdpbGwgc2F5IHllcywgYW5kIHlvdSBtaWdodCBldmVuIHNwYXJrIGEgcGFzc2lvbmF0ZSBkZWJhdGUuCgpEZXNwaXRlIGl0cyBsYWNrIG9mIHBvcHVsYXJpdHkgaW4gdGhlIGRhdGEgc2NpZW5jZSBjb21tdW5pdHksIEdBTSBpcyBhIHBvd2VyZnVsIGFuZCB5ZXQgc2ltcGxlIHRlY2huaXF1ZS4gSGVuY2UsIHRoZSBwdXJwb3NlIG9mIHRoaXMgcG9zdCBpcyB0byBjb252aW5jZSBtb3JlIGRhdGEgc2NpZW50aXN0cyB0byB1c2UgR0FNLiBPZiBjb3Vyc2UsIEdBTSBpcyBubyBzaWx2ZXIgYnVsbGV0LCBidXQgaXQgaXMgYSB0ZWNobmlxdWUgeW91IHNob3VsZCBhZGQgdG8geW91ciBhcnNlbmFsLiBIZXJlIGFyZSB0aHJlZSBrZXkgcmVhc29uczoKCkVhc3kgdG8gaW50ZXJwcmV0LgoKRmxleGlibGUgcHJlZGljdG9yIGZ1bmN0aW9ucyBjYW4gdW5jb3ZlciBoaWRkZW4gcGF0dGVybnMgaW4gdGhlIGRhdGEuCgpSZWd1bGFyaXphdGlvbiBvZiBwcmVkaWN0b3IgZnVuY3Rpb25zIGhlbHBzIGF2b2lkIG92ZXJmaXR0aW5nLgoKICBJbiBnZW5lcmFsLCBHQU0gaGFzIHRoZSBpbnRlcnByZXRhYmlsaXR5IGFkdmFudGFnZXMgb2YgR0xNcyB3aGVyZSB0aGUgY29udHJpYnV0aW9uIG9mIGVhY2ggaW5kZXBlbmRlbnQgdmFyaWFibGUgdG8gdGhlIHByZWRpY3Rpb24gaXMgY2xlYXJseSBlbmNvZGVkLiBIb3dldmVyLCBpdCBoYXMgc3Vic3RhbnRpYWxseSBtb3JlIGZsZXhpYmlsaXR5IGJlY2F1c2UgdGhlIHJlbGF0aW9uc2hpcHMgYmV0d2VlbiBpbmRlcGVuZGVudCBhbmQgZGVwZW5kZW50IHZhcmlhYmxlIGFyZSBub3QgYXNzdW1lZCB0byBiZSBsaW5lYXIuIEluIGZhY3QsIHdlIGRvbuKAmXQgaGF2ZSB0byBrbm93IGEgcHJpb3JpIHdoYXQgdHlwZSBvZiBwcmVkaWN0aXZlIGZ1bmN0aW9ucyB3ZSB3aWxsIGV2ZW50dWFsbHkgbmVlZC4gRnJvbSBhbiBlc3RpbWF0aW9uIHN0YW5kcG9pbnQsIHRoZSB1c2Ugb2YgcmVndWxhcml6ZWQsIG5vbnBhcmFtZXRyaWMgZnVuY3Rpb25zIGF2b2lkcyB0aGUgcGl0ZmFsbHMgb2YgZGVhbGluZyB3aXRoIGhpZ2hlciBvcmRlciBwb2x5bm9taWFsIHRlcm1zIGluIGxpbmVhciBtb2RlbHMuIEZyb20gYW4gYWNjdXJhY3kgc3RhbmRwb2ludCwgR0FNcyBhcmUgY29tcGV0aXRpdmUgd2l0aCBwb3B1bGFyIGxlYXJuaW5nIHRlY2huaXF1ZXMuCgpHZW5lcmFsaXplZCBhZGRpdGl2ZSBtb2RlbHMgd2VyZSBvcmlnaW5hbGx5IGludmVudGVkIGJ5IFRyZXZvciBIYXN0aWUgYW5kIFJvYmVydCBUaWJzaGlyYW5pIGluIDE5ODYgKHNlZSBbMV0sIFsyXSkuIFRoZSBHQU0gZnJhbWV3b3JrIGlzIGJhc2VkIG9uIGFuIGFwcGVhbGluZyBhbmQgc2ltcGxlIG1lbnRhbCBtb2RlbDoKCiAgICBSZWxhdGlvbnNoaXBzIGJldHdlZW4gdGhlIGluZGl2aWR1YWwgcHJlZGljdG9ycyBhbmQgdGhlIGRlcGVuZGVudCB2YXJpYWJsZSBmb2xsb3cgc21vb3RoIHBhdHRlcm5zIHRoYXQgY2FuIGJlIGxpbmVhciBvciBub25saW5lYXIuCgogICAgV2UgY2FuIGVzdGltYXRlIHRoZXNlIHNtb290aCByZWxhdGlvbnNoaXBzIHNpbXVsdGFuZW91c2x5IGFuZCB0aGVuIHByZWRpY3QgJCRnKEUoWSkpKSQkCiBieSBzaW1wbHkgYWRkaW5nIHRoZW0gdXAuCgpNYXRoZW1hdGljYWxseSBzcGVha2luZywgR0FNIGlzIGFuIGFkZGl0aXZlIG1vZGVsaW5nIHRlY2huaXF1ZSB3aGVyZSB0aGUgaW1wYWN0IG9mIHRoZSBwcmVkaWN0aXZlIHZhcmlhYmxlcyBpcyBjYXB0dXJlZCB0aHJvdWdoIHNtb290aCBmdW5jdGlvbnMgd2hpY2jigJRkZXBlbmRpbmcgb24gdGhlIHVuZGVybHlpbmcgcGF0dGVybnMgaW4gdGhlIGRhdGHigJRjYW4gYmUgbm9ubGluZWFyLgoKUmVmZXJlbmNlcwoKWzFdIEhhc3RpZSwgVHJldm9yIGFuZCBUaWJzaGlyYW5pLCBSb2JlcnQuICgxOTkwKSwgR2VuZXJhbGl6ZWQgQWRkaXRpdmUgTW9kZWxzLCBOZXcgWW9yazogQ2hhcG1hbiBhbmQgSGFsbC4KClsyXSBIYXN0aWUsIFRyZXZvciBhbmQgVGlic2hpcmFuaSwgUm9iZXJ0LiAoMTk4NiksIEdlbmVyYWxpemVkIEFkZGl0aXZlIE1vZGVscywgU3RhdGlzdGljYWwgU2NpZW5jZSwgVm9sLiAxLCBObyAzLCAyOTctMzE4LgoKWzNdIFdvb2QsIFMuIE4uICgyMDA2KSwgR2VuZXJhbGl6ZWQgQWRkaXRpdmUgTW9kZWxzOiBhbiBpbnRyb2R1Y3Rpb24gd2l0aCBSLCBCb2NhIFJhdG9uOiBDaGFwbWFuICYgSGFsbC9DUkMKCls0XSBXb29kLCBTLiBOLiAoMjAwNCkuIFN0YWJsZSBhbmQgZWZmaWNpZW50IG11bHRpcGxlIHNtb290aGluZyBwYXJhbWV0ZXIgZXN0aW1hdGlvbiBmb3IgZ2VuZXJhbGl6ZWQgYWRkaXRpdmUgbW9kZWxzLiBKb3VybmFsIG9mIHRoZSBBbWVyaWNhbiBTdGF0aXN0aWNhbCBBc3NvY2lhdGlvbiA5OSwgNjcz4oCTNjg2CgpbNV0gTWFyeCwgQnJpYW4gRCBhbmQgRWlsZXJzLCBQYXVsIEguQy4gKDE5OTgpLiBEaXJlY3QgZ2VuZXJhbGl6ZWQgYWRkaXRpdmUgbW9kZWxpbmcgd2l0aCBwZW5hbGl6ZWQgbGlrZWxpaG9vZCwgQ29tcHV0YXRpb25hbCBTdGF0aXN0aWNzICYgRGF0YSBBbmFseXNpcyAyOCAoMTk5OCkgMTkzLTIwCgpbNl0gU2luaGEsIFNhbWlyYW4sIEEgdmVyeSBzaG9ydCBub3RlIG9uIEItc3BsaW5lcywgaHR0cDovL3d3dy5zdGF0LnRhbXUuZWR1L35zaW5oYS9yZXNlYXJjaC9ub3RlMS5bUERGXSgvYXNzZXRzL2ZpbGVzL2dhbS5wZGYpCgpbN10gR2VybWFuIFJvZHLEsWd1ZXogKDIwMDEpLCBTbW9vdGhpbmcgYW5kIE5vbi1QYXJhbWV0cmljIFJlZ3Jlc3Npb24sIGh0dHA6Ly9kYXRhLnByaW5jZXRvbi5lZHUvZWNvNTcyL3Ntb290aGluZy5wZAoKWzhdIE5vdGVzIG9uIEdBTSBCeSBTaW1vbiBXb29kLiBodHRwOi8vcGVvcGxlLmJhdGguYWMudWsvc3cyODMvbWdjdi90YW1wZXJlL2dhbS5bUERGXSgvYXNzZXRzL2ZpbGVzL2dhbS5wZGYpCgpbOV0gTm90ZXMgb24gU21vb3RoaW5nIFBhcmFtZXRlciBTZWxlY3Rpb24gQnkgU2ltb24gV29vZCwgaHR0cDovL3Blb3BsZS5iYXRoLmFjLnVrL3N3MjgzL21nY3YvdGFtcGVyZS9zbW9vdGhuZXNzLltQREZdKC9hc3NldHMvZmlsZXMvZ2FtLnBkZikKClsxMF0gTm90ZXMgb24gUkVNTCAmIEdBTSBCeSBTaW1vbiBXb29kLCBodHRwOi8vcGVvcGxlLmJhdGguYWMudWsvc3cyODMvdGFsa3MvUkVNTC5bUERGXSgvYXNzZXRzL2ZpbGVzL2dhbS5wZGYpCgpbMTFdIEthcmF0em9nbG91LCBBbGV4YW5kcm9zLCBNZXllciwgRGF2aWQgYW5kIEhvcm5paywgS3VydCAoMjAwNiksIFN1cHBvcnQgVmVjdG9yIE1hY2hpbmVzIGluIFIsIEpvdXJuYWwgb2YgU3RhdGlzdGljYWwgU29mdHdhcmUgVm9sdW1lIDE1LCBJc3N1ZSA5LCBodHRwOi8vd3d3LmpzdGF0c29mdC5vcmcvdjE1L2kwOS9wYXBlcgoKWzEyXSDigJxlMTA3MeKAnSBwYWNrYWdlLCBodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvZTEwNzEvZTEwNzEuW1BERl0oL2Fzc2V0cy9maWxlcy9nYW0ucGRmKQoKWzEzXSDigJxtZ2N24oCdIHBhY2thZ2UsIGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9tZ2N2L21nY3YuW1BERl0oL2Fzc2V0cy9maWxlcy9nYW0ucGRmKQoKWzE0XSDigJxnYW3igJ0gcGFja2FnZSwgaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL2dhbS9nYW0uW1BERl0oL2Fzc2V0cy9maWxlcy9nYW0ucGRmKQoKWzE1XSDigJxyYW5kb21Gb3Jlc3RTUkPigJ0gcGFja2FnZSwgaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL3JhbmRvbUZvcmVzdFNSQy9yYW5kb21Gb3Jlc3RTUkMuW1BERl0oL2Fzc2V0cy9maWxlcy9nYW0ucGRmKQoKb3IgZnJvbTogaHR0cDovL3BsYW50ZWNvbG9neS5zeXIuZWR1L2ZyaWRsZXkvYmlvNzkzL2dhbS5odG1sCgpHQU1zIGluIFIgYXJlIGEgbm9ucGFyYW1ldHJpYyBleHRlbnNpb24gb2YgR0xNcywgdXNlZCBvZnRlbiBmb3IgdGhlIGNhc2Ugd2hlbiB5b3UgaGF2ZSBubyBhIHByaW9yaSByZWFzb24gZm9yIGNob29zaW5nIGEgcGFydGljdWxhciByZXNwb25zZSBmdW5jdGlvbiAoc3VjaCBhcyBsaW5lYXIsIHF1YWRyYXRpYywgZXRjLikgYW5kIHdhbnQgdGhlIGRhdGEgdG8gJ3NwZWFrIGZvciB0aGVtc2VsdmVzJy4gIEdBTXMgZG8gdGhpcyB2aWEgYSBzbW9vdGhpbmcgZnVuY3Rpb24sIHNpbWlsYXIgdG8gd2hhdCB5b3UgbWF5IGFscmVhZHkga25vdyBhYm91dCBsb2NhbGx5IHdlaWdodGVkIHJlZ3Jlc3Npb25zLiAgR0FNcyB0YWtlIGVhY2ggcHJlZGljdG9yIHZhcmlhYmxlIGluIHRoZSBtb2RlbCBhbmQgc2VwYXJhdGUgaXQgaW50byBzZWN0aW9ucyAoZGVsaW1pdGVkIGJ5ICdrbm90cycpLCBhbmQgdGhlbiBmaXQgcG9seW5vbWlhbCBmdW5jdGlvbnMgdG8gZWFjaCBzZWN0aW9uIHNlcGFyYXRlbHksIHdpdGggdGhlIGNvbnN0cmFpbnQgdGhhdCB0aGVyZSBhcmUgbm8ga2lua3MgYXQgdGhlIGtub3RzIChzZWNvbmQgZGVyaXZhdGl2ZXMgb2YgdGhlIHNlcGFyYXRlIGZ1bmN0aW9ucyBhcmUgZXF1YWwgYXQgdGhlIGtub3RzKS4gIFRoZSBudW1iZXIgb2YgcGFyYW1ldGVycyB1c2VkIGZvciBzdWNoIGZpdHRpbmcgaXMgb2J2aW91c2x5IG1vcmUgdGhhbiB3aGF0IHdvdWxkIGJlIG5lY2Vzc2FyeSBmb3IgYSBzaW1wbGVyIHBhcmFtZXRyaWMgZml0IHRvIHRoZSBzYW1lIGRhdGEsIGJ1dCBjb21wdXRhdGlvbmFsIHNob3J0Y3V0cyBtZWFuIHRoZSBtb2RlbCBkZWdyZWVzIG9mIGZyZWVkb20gaXMgdXN1YWxseSBsb3dlciB0aGFuIHdoYXQgeW91IG1pZ2h0IGV4cGVjdCBmcm9tIGEgbGluZSB3aXRoIHNvIG11Y2ggJ3dpZ2dsaW5lc3MnLiAgSW5kZWVkIHRoaXMgaXMgdGhlIHByaW5jaXBhbCBzdGF0aXN0aWNhbCBpc3N1ZSBhc3NvY2lhdGVkIHdpdGggR0FNIG1vZGVsaW5nOiBtaW5pbWl6aW5nIHJlc2lkdWFsIGRldmlhbmNlIChnb29kbmVzcyBvZiBmaXQpIHdoaWxlIG1heGltaXppbmcgcGFyc2ltb255IChsb3dlc3QgcG9zc2libGUgZGVncmVlcyBvZiBmcmVlZG9tKS4gIFNpbmNlIHRoZSBtb2RlbCBmaXQgaXMgYmFzZWQgb24gZGV2aWFuY2UvbGlrZWxpaG9vZCwgZml0dGVkIG1vZGVscyBhcmUgZGlyZWN0bHkgY29tcGFyYWJsZSB3aXRoIEdMTXMgdXNpbmcgbGlrZWxpaG9vZCB0ZWNobmlxdWVzIChsaWtlIEFJQykgb3IgY2xhc3NpY2FsIHRlc3RzIGJhc2VkIG9uIG1vZGVsIGRldmlhbmNlIChDaGktc3F1YXJlZCBvciBGIHRlc3RzLCBkZXBlbmRpbmcgb24gdGhlIGVycm9yIHN0cnVjdHVyZSkuICBFdmVuIGJldHRlciwgYWxsIHRoZSBlcnJvciBhbmQgbGluayBzdHJ1Y3R1cmVzIG9mIEdMTXMgYXJlIGF2YWlsYWJsZSBpbiBHQU1zIChpbmNsdWRpbmcgcG9pc3NvbiBhbmQgYmlub21pYWwpLCBhcyBhcmUgdGhlIHN0YW5kYXJkIHN1aXRlIG9mIGxtIG9yIGdsbSBhdHRyaWJ1dGVzIChyZXNpZCwgZml0dGVkLCBzdW1tYXJ5LCBjb2VmLCBldGMuKS4gIEEgcHJpbmNpcGFsIHJlYXNvbiB3aHkgR0FNcyBhcmUgb2Z0ZW4gbGVzcyBwcmVmZXJyZWQgdGhhbiBHTE1zIGlzIHRoYXQgdGhlIHJlc3VsdHMgYXJlIG9mdGVuIGRpZmZpY3VsdCB0byBpbnRlcnByZXQgYmVjYXVzZSBubyBwYXJhbWV0ZXIgdmFsdWVzIGFyZSByZXR1cm5lZCAoYWx0aG91Z2ggc2lnbmlmaWNhbmNlIHRlc3RzIG9mIGVhY2ggdGVybSBhcmUpLiAgVGhleSBjYW4gYmUgdmVyeSBnb29kIGZvciBwcmVkaWN0aW9uL2ludGVycG9sYXRpb24sIGFzIHdlbGwgYXMgZXhwbG9yYXRvcnkgYW5hbHlzZXMgYWJvdXQgdGhlIGZ1bmN0aW9uYWwgbmF0dXJlIG9mIGEgcmVzcG9uc2UuICBTb21lIHJlc2VhcmNoZXJzIGV4YW1pbmUgdGhlIHNoYXBlIG9mIGEgY3VydmUgd2l0aCBHQU1zLCB0aGVuIHJlY29uc3RydWN0IHRoZSBjdXJ2ZSBzaGFwZSBwYXJhbWV0cmljYWxseSB3aXRoIEdMTXMgZm9yIG1vZGVsIGJ1aWxkaW5nLgogICAgICAgICAgICBUaGVyZSBhcmUgdHdvIGNvbW1vbiBpbXBsZW1lbnRhdGlvbnMgb2YgR0FNcyBpbiBSLiAgVGhlIG9sZGVyIHZlcnNpb24gKG9yaWdpbmFsbHkgbWFkZSBmb3IgUy1QTFVTKSBpcyBhdmFpbGFibGUgYXMgdGhlICdnYW0nIHBhY2thZ2UgYnkgSGFzdGllIGFuZCBUaWJzaGlyYW5pLiAgVGhlIG5ld2VyIHZlcnNpb24gdGhhdCB3ZSB3aWxsIHVzZSBiZWxvdyBpcyB0aGUgJ21nY3YnIHBhY2thZ2UgZnJvbSBTaW1vbiBXb29kLiAgVGhlIGJhc2ljIG1vZGVsaW5nIHByb2NlZHVyZSBmb3IgYm90aCBwYWNrYWdlcyBpcyBzaW1pbGFyICh0aGUgZnVuY3Rpb24gaXMgZ2FtIGZvciBib3RoOyBiZSB3YXJ5IG9mIGhhdmluZyBib3RoIGxpYnJhcmllcyBsb2FkZWQgYXQgdGhlIHNhbWUgdGltZSksIGJ1dCB0aGUgYmVoaW5kLXRoZS1zY2VuZXMgY29tcHV0YXRpb25hbCBhcHByb2FjaGVzIGRpZmZlciwgYXMgZG8gdGhlIGFyZ3VtZW50cyBmb3Igb3B0aW1pemF0aW9uIGFuZCB0aGUgbW9kZWwgb3V0cHV0LiAgRXhwZWN0IHRoZSByZXN1bHRzIHRvIGJlIHNsaWdodGx5IGRpZmZlcmVudCB3aGVuIHVzZWQgd2l0aCB0aGUgc2FtZSBtb2RlbCBzdHJ1Y3R1cmUgb24gdGhlIHNhbWUgZGF0YXNldC4iCiAgCiAgCiAgCiAgVGhlc2UgYXJlIHRoZSBtb3N0IHVuaW5mb3JtZWQgbW9kZWxzIHlvdSBjYW4gdXNlLiBXZSBjYW4gY2VydGFpbmx5IHRyeSB0byBhZGQgc29tZSBwcm9jZXNzIHRvIHRoZXNlIG1vZGVscyBhbmQgdHJ5IHRvIGdldCBzb21lIG1vcmUgcmVsaWFibGUgbnVtYmVycywgYnV0IHRoaXMgd2lsbCBpbnZvbHZlIG1vcmUgd29yay4gCgoKIyMjIEZpdCBhbmQgcGxvdCBHQU0gbW9kZWwgd2l0aCBkaWZmZXJlbnQgZGVncmVlcyBvZiBmcmVlZG9tCldlIHNob3VsZCBtYWtlIG91ciBkZWNpc2lvbnMgdmVyeSB0cmFuc3BhcmVudCBoZXJlLiBXZSBzaG91bGQgYmUgYWJsZSB0byBqdXN0aWZ5IG91ciBkZWNpc2lvbiBvZiAzIGRlZ3JlZXMgb2YgZnJlZWRvbSBvdmVyIG90aGVyIHBvc3NpYmxlIHZhbHVlcy4gCgojIyMjIERlbnNpdHkgcHJvamVjdGlvbnMKCmBgYHtyLCBjYWNoZT1UUlVFfQojIFJlYWQgdGhlIHBvbHlnb25zCmxpYnJhcnkocmdkYWwpCmdldHdkKCkKb3JpZ2lucyA8LSByZWFkU2hhcGVQb2x5KCdPcmlnaW5zX3VwZGF0ZWQuc2hwJykKcHJvajRzdHJpbmcob3JpZ2lucykgCgpgYGAKCgpgYGB7cn0KIyBFeHRyYWN0IGRhdGEKbGlicmFyeShyYXN0ZXIpCmUgPC0gZXh0ZW50KC0xODAsIDE4MCwgLTYwLCA4NCkKYWxsX2NlbGxzIDwtIGV4dHJhY3QociwgZSwgY2VsbG51bWJlciA9IFRSVUUpCgpwZXIub3JpZ2luIDwtIGV4dHJhY3Qociwgb3JpZ2lucywgY2VsbG51bWJlciA9IFRSVUUsIGJ1ZmZlciA9IDEwMDAwMCkKCmxlbmd0aChhbGxfY2VsbHMpCmZvcihpIGluIDE6MjApewphbGxfY2VsbHMgPC0gYWxsX2NlbGxzWy13aGljaChwZXIub3JpZ2luW1tpXV1bLDFdICVpbiUgYWxsX2NlbGxzWywxXSksIF0KfQpsZW5ndGgoYWxsX2NlbGxzKQoKbmFtZXMocGVyLm9yaWdpbikgPC0gb3JpZ2luc0BkYXRhWywgMV0KCnN0cihhbGxfY2VsbHMpCnN0cihwZXIub3JpZ2luKQpgYGAKCmBgYHtyfQpvcmlnaW5fdmVjdG9ycyA8LSByZXAoTkEsIDMpCgpmb3IoaCBpbiAxOjIwKXsKb3JpZ2luSSA8LSBQb3BbcGVyLm9yaWdpbltbaF1dWywgMV0sIF0KY2VsbF92ZWN0b3IgPC0gIGFzLnZlY3RvcihwZXIub3JpZ2luW1toXV1bLCAxXSkKeF92YWx1ZXMgPC0gbWF0cml4KGMoNDoyMSksIGRpbShvcmlnaW5JKVsxXSwgMTgsIGJ5cm93PVRSVUUpCnhfdmFsdWVfdmVjdG9yIDwtIGFzLnZlY3Rvcih4X3ZhbHVlcykKeV92YWx1ZV92ZWN0b3IgPC0gYXMudmVjdG9yKG9yaWdpbkkpCgphbGxfdmVjdG9yc19wcmUgPC0gY2JpbmQocmVwKG5hbWVzKHBlci5vcmlnaW4pW2hdLCBsZW5ndGgoeF92YWx1ZV92ZWN0b3IpKSx4X3ZhbHVlX3ZlY3RvciwgeV92YWx1ZV92ZWN0b3IsIGNlbGxfdmVjdG9yKQpvcmlnaW5fdmVjdG9ycyA8LSByYmluZChvcmlnaW5fdmVjdG9ycywgYWxsX3ZlY3RvcnNfcHJlKQp9CgojbmFtZXMob3JpZ2luX3ZlY3RvcnMpWzFdIDwtICJsb2NhdGlvbiIKb3JpZ2luX3ZlY3RvcnMgPC0gb3JpZ2luX3ZlY3RvcnNbLTEsXQpoZWFkKG9yaWdpbl92ZWN0b3JzKQpgYGAKCmBgYHtyfQojbm90IGluIG9yaWdpbgoKQWxsIDwtIFBvcFthbGxfY2VsbHNbLCAxXSwgXQpjZWxsX3ZlY3RvciA8LSAgYXMudmVjdG9yKEFsbFssIDFdKQp4X3ZhbHVlcyA8LSBtYXRyaXgoYyg0OjIxKSwgZGltKEFsbClbMV0sIDE4LCBieXJvdz1UUlVFKQp4X3ZhbHVlX3ZlY3RvciA8LSBhcy52ZWN0b3IoeF92YWx1ZXMpCnlfdmFsdWVfdmVjdG9yIDwtIGFzLnZlY3RvcihBbGwpCgphbGxfdmVjdG9yc19wcmUgPC0gY2JpbmQocmVwKCJub3Rfb3JpZ2luIiwgbGVuZ3RoKHhfdmFsdWVfdmVjdG9yKSkseF92YWx1ZV92ZWN0b3IsIHlfdmFsdWVfdmVjdG9yLCBjZWxsX3ZlY3RvcikKYWxsX3ZlY3RvcnMgPC0gYXMuZGF0YS5mcmFtZShyYmluZChvcmlnaW5fdmVjdG9ycywgYWxsX3ZlY3RvcnNfcHJlKSkKbGV2ZWxzKGFsbF92ZWN0b3JzWywxXSkKYGBgCgpgYGB7cn0KCmFsbF92ZWN0b3JzIDwtIGNiaW5kKGFsbF92ZWN0b3JzLCBzY2FsZShhcy5udW1lcmljKGFsbF92ZWN0b3JzWywzXSkpKQpjb2xuYW1lcyhhbGxfdmVjdG9ycykgPC0gYygibG9jYXRpb25fbmFtZSIsICJ4X3ZhbHVlcyIsICJkZW5zaXR5X3ZhbHVlcyIsICJjZWxsX0lEIiwgInNjYWxlZF9kZW5zaXR5X3ZhbHVlcyIpCmFsbF92ZWN0b3JzIDwtIGFsbF92ZWN0b3JzWywgYygxLDIsMyw1LDQpXQpkaW0oYWxsX3ZlY3RvcnMpCmBgYAoKCgpgYGB7ciwgY2FjaGU9VFJVRX0KCm9yaWdpbnMgPC0gc3Vic2V0KGFsbF92ZWN0b3JzLCBsb2NhdGlvbl9uYW1lICE9ICJub3Rfb3JpZ2luIikKCnBhcihtZnJvdz1jKDQsNiksIG1hcj1jKDAsMCwwLDApLCB4YXh0PSJuIikKZm9yKGggaW4gMToyMCl7CiNoIDwtIDMKZGVuc2l0eV90cmVuZCA8LSBvcmlnaW5zW3doaWNoKG9yaWdpbnNbLDFdID09IG5hbWVzKHBlci5vcmlnaW4pW2hdKSxdCgpwbG90KGFzLm51bWVyaWMoZGVuc2l0eV90cmVuZFssMl0pLCBhcy5udW1lcmljKGRlbnNpdHlfdHJlbmRbLDRdKSwgY29sPWFkanVzdGNvbG9yKCJjb3JuZmxvd2VyYmx1ZSIsIGFscGhhPTAuOCksICB4bGFiPSJ5ZWFyIiwgeWxhYj0iRGVuc2l0eSIsIHlsaW09YygtNSw1KSwgeGxpbT1jKDQsMjEpLCB0eXBlPSJuIikKCnBvaW50cyhhcy5udW1lcmljKGRlbnNpdHlfdHJlbmRbLDJdKSwgYXMubnVtZXJpYyhkZW5zaXR5X3RyZW5kWyw0XSksIGNleD0wLjUsIGNvbD1hZGp1c3Rjb2xvcigiY29ybmZsb3dlcmJsdWUiLCAwLjUpKQptdGV4dChhcy5jaGFyYWN0ZXIoZGVuc2l0eV90cmVuZFsxLDFdKSwgMywgbGluZT0tMSwgY2V4PTAuNSkKfQoKI2FsbCBvcmlnaW5zCnBsb3QoYXMubnVtZXJpYyhkZW5zaXR5X3RyZW5kWywyXSksIGFzLm51bWVyaWMoZGVuc2l0eV90cmVuZFssNF0pLCBjb2w9YWRqdXN0Y29sb3IoImNvcm5mbG93ZXJibHVlIiwgYWxwaGE9MC44KSwgIHhsYWI9InllYXIiLCB5bGFiPSJEZW5zaXR5IiwgeWxpbT1jKC01LDUpLCB4bGltPWMoNCwyMSksIHR5cGU9Im4iKQoKcG9pbnRzKGFzLm51bWVyaWMob3JpZ2luc1ssMl0pLCBhcy5udW1lcmljKG9yaWdpbnNbLDRdKSwgY2V4PTAuNSwgY29sPWFkanVzdGNvbG9yKCJjb3JuZmxvd2VyYmx1ZSIsIDAuNSkpCm10ZXh0KCJhbGwgb3JpZ2lucyIsIDMsIGxpbmU9LTEsIGNleD0wLjUpCgojbm90IG9yaWdpbnMKbm90X29yaWdpbnMgPC0gc3Vic2V0KGFsbF92ZWN0b3JzLCBsb2NhdGlvbl9uYW1lID09ICJub3Rfb3JpZ2luIikKcGxvdChhcy5udW1lcmljKG5vdF9vcmlnaW5zWywyXSksIGFzLm51bWVyaWMobm90X29yaWdpbnNbLDRdKSwgY29sPWFkanVzdGNvbG9yKCJjb3JuZmxvd2VyYmx1ZSIsIGFscGhhPTAuOCksICB4bGFiPSJ5ZWFyIiwgeWxhYj0iRGVuc2l0eSIsIHlsaW09YygtNSw1KSwgeGxpbT1jKDQsMjEpLCB0eXBlPSJuIikKCnBvaW50cyhhcy5udW1lcmljKG5vdF9vcmlnaW5zWywyXSksIGFzLm51bWVyaWMobm90X29yaWdpbnNbLDRdKSwgY2V4PTAuNSwgY29sPWFkanVzdGNvbG9yKCJjb3JuZmxvd2VyYmx1ZSIsIDAuNSkpCm10ZXh0KCJldmVyeXRoaW5nIG90aGVyIHRoYW4gb3JpZ2lucyIsIDMsIGxpbmU9LTEsIGNleD0wLjUpCmBgYAoKCgoKYGBge3IsIGNhY2hlPVRSVUV9CgpwYXIobWZyb3c9Yyg0LDYpLCBtYXI9YygwLDAsMCwwKSwgeGF4dD0ibiIpCmZvcihoIGluIDE6MjApewojaCA8LSAzCmRlbnNpdHlfdHJlbmQgPC0gb3JpZ2luc1t3aGljaChvcmlnaW5zWywxXSA9PSBuYW1lcyhwZXIub3JpZ2luKVtoXSksXQoKcGxvdChhcy5udW1lcmljKGRlbnNpdHlfdHJlbmRbLDJdKSwgYXMubnVtZXJpYyhkZW5zaXR5X3RyZW5kWyw0XSksIGNvbD1hZGp1c3Rjb2xvcigiY29ybmZsb3dlcmJsdWUiLCBhbHBoYT0wLjgpLCAgeGxhYj0ieWVhciIsIHlsYWI9IkRlbnNpdHkiLCB5bGltPWMoLTIsMiksIHhsaW09Yyg0LDIxKSwgdHlwZT0ibiIpCgpmb3IoZyBpbiBsZXZlbHMoYXMuZmFjdG9yKGRlbnNpdHlfdHJlbmRbLDVdKSkpewpzdWJiZWQgPC0gc3Vic2V0KGRlbnNpdHlfdHJlbmQsIGNlbGxfSUQgPT0gZykKb3JkZXJlZF9zdWJiZWQgPC0gc3ViYmVkW29yZGVyKHN1YmJlZCR4X3ZhbHVlcyksIF0KbGluZXMoYXMubnVtZXJpYyhvcmRlcmVkX3N1YmJlZFssMl0pLCBhcy5udW1lcmljKG9yZGVyZWRfc3ViYmVkWyw0XSksIGx3ZD0uMywgY29sPWFkanVzdGNvbG9yKCJjb3JuZmxvd2VyYmx1ZSIsIDAuOCkpCn0KbXRleHQoYXMuY2hhcmFjdGVyKGRlbnNpdHlfdHJlbmRbMSwxXSksIDMsIGxpbmU9LTEsIGNleD0wLjUpCn0KCiNhbGwgb3JpZ2lucwpwbG90KGFzLm51bWVyaWMoZGVuc2l0eV90cmVuZFssMl0pLCBhcy5udW1lcmljKGRlbnNpdHlfdHJlbmRbLDRdKSwgY29sPWFkanVzdGNvbG9yKCJjb3JuZmxvd2VyYmx1ZSIsIGFscGhhPTAuOCksICB4bGFiPSJ5ZWFyIiwgeWxhYj0iRGVuc2l0eSIsIHlsaW09YygtMiwyKSwgeGxpbT1jKDQsMjEpLCB0eXBlPSJuIikKCmZvcihnIGluIGxldmVscyhhcy5mYWN0b3IoZGVuc2l0eV90cmVuZFssNV0pKSl7CnN1YmJlZCA8LSBzdWJzZXQoZGVuc2l0eV90cmVuZCwgY2VsbF9JRCA9PSBnKQpvcmRlcmVkX3N1YmJlZCA8LSBzdWJiZWRbb3JkZXIoc3ViYmVkJHhfdmFsdWVzKSwgXQpsaW5lcyhhcy5udW1lcmljKG9yZGVyZWRfc3ViYmVkWywyXSksIGFzLm51bWVyaWMob3JkZXJlZF9zdWJiZWRbLDRdKSwgbHdkPS4zLCBjb2w9YWRqdXN0Y29sb3IoImNvcm5mbG93ZXJibHVlIiwgMC44KSkKfQptdGV4dCgiYWxsIG9yaWdpbnMiLCAzLCBsaW5lPS0xLCBjZXg9MC41KQoKCmBgYAoKCgpgYGB7ciwgY2FjaGU9VFJVRX0KcGFyKG1mcm93PWMoNCw2KSwgbWFyPWMoMCwwLDAsMCkpCmZvcihoIGluIDE6MjApewojaCA8LSAzCmRlbnNpdHlfdHJlbmQgPC0gYWxsX3ZlY3RvcnNbd2hpY2goYWxsX3ZlY3RvcnNbLDFdID09IG5hbWVzKHBlci5vcmlnaW4pW2hdKSxdCgpwbG90KGFzLm51bWVyaWMoZGVuc2l0eV90cmVuZFssMl0pLCBhcy5udW1lcmljKGRlbnNpdHlfdHJlbmRbLDRdKSwgY29sPWFkanVzdGNvbG9yKCJjb3JuZmxvd2VyYmx1ZSIsIGFscGhhPTAuOCksICB4bGFiPSJ5ZWFyIiwgeWxhYj0iRGVuc2l0eSIsIHlsaW09YygtMywzKSwgeGxpbT1jKDQsMjEpLCB0eXBlPSJuIiwgeGF4dD0ibiIsIHlheHQ9Im4iKQoKcG9pbnRzKGFzLm51bWVyaWMoZGVuc2l0eV90cmVuZFssMl0pLCBhcy5udW1lcmljKGRlbnNpdHlfdHJlbmRbLDRdKSwgY2V4PTAuNSwgY29sPWFkanVzdGNvbG9yKCJncmV5IiwgMC41KSkKCgpvcmRlcmVkX2RlbnNpdHlfdHJlbmQgPC0gZGVuc2l0eV90cmVuZFtvcmRlcihkZW5zaXR5X3RyZW5kJHhfdmFsdWVzKSwgXQoKCmdhbW1lciA8LSBsb2Vzcyhhcy5udW1lcmljKG9yZGVyZWRfZGVuc2l0eV90cmVuZFssNF0pIH4gYXMubnVtZXJpYyhvcmRlcmVkX2RlbnNpdHlfdHJlbmRbLDJdKSkKc3VtbWFyeShnYW1tZXIpCgoKbGluZXMoYXMubnVtZXJpYyhvcmRlcmVkX2RlbnNpdHlfdHJlbmRbLDJdKSAscHJlZGljdChnYW1tZXIpLCAgY29sPSJjb3JuZmxvd2VyYmx1ZSIsIGx3ZD0yKQptdGV4dChhcy5jaGFyYWN0ZXIob3JkZXJlZF9kZW5zaXR5X3RyZW5kWzEsMV0pLCAzLCBsaW5lPS0xLCBjZXg9MC41KQp9CgpkZW5zaXR5X3RyZW5kIDwtIGFsbF92ZWN0b3JzWy0xLF0KCnBsb3QoYXMubnVtZXJpYyhkZW5zaXR5X3RyZW5kWywyXSksIGFzLm51bWVyaWMoZGVuc2l0eV90cmVuZFssNF0pLCBjb2w9YWRqdXN0Y29sb3IoImNvcm5mbG93ZXJibHVlIiwgYWxwaGE9MC44KSwgIHhsYWI9InllYXIiLCB5bGFiPSJEZW5zaXR5IiwgeWxpbT1jKC0zLDMpLCB4bGltPWMoNCwyMSksIHR5cGU9Im4iLCB4YXh0PSJuIiwgeWF4dD0ibiIpCgpwb2ludHMoYXMubnVtZXJpYyhkZW5zaXR5X3RyZW5kWywyXSksIGFzLm51bWVyaWMoZGVuc2l0eV90cmVuZFssNF0pLCBjZXg9MC41LCBjb2w9YWRqdXN0Y29sb3IoImdyZXkiLCAwLjUpKQoKCm9yZGVyZWRfZGVuc2l0eV90cmVuZCA8LSBkZW5zaXR5X3RyZW5kW29yZGVyKGRlbnNpdHlfdHJlbmQkeF92YWx1ZXMpLCBdCgoKZ2FtbWVyIDwtIGxvZXNzKGFzLm51bWVyaWMob3JkZXJlZF9kZW5zaXR5X3RyZW5kWyw0XSkgfiBhcy5udW1lcmljKG9yZGVyZWRfZGVuc2l0eV90cmVuZFssMl0pKQpzdW1tYXJ5KGdhbW1lcikKCgpsZW5ndGgob3JkZXJlZF9kZW5zaXR5X3RyZW5kWywyXSkKbGVuZ3RoKHByZWRpY3QoZ2FtbWVyKSkKCmxpbmVzKGFzLm51bWVyaWMob3JkZXJlZF9kZW5zaXR5X3RyZW5kWywyXSkgLHByZWRpY3QoZ2FtbWVyKSwgIGNvbD0iY29ybmZsb3dlcmJsdWUiLCBsd2Q9MikKbXRleHQoImFsbCBvcmlnaW5zIiwgMywgbGluZT0tMSwgY2V4PTAuNSkKCmBgYAoKc3BhbgkKdGhlIHBhcmFtZXRlciDOsSB3aGljaCBjb250cm9scyB0aGUgZGVncmVlIG9mIHNtb290aGluZy4KClRoZSBzaXplIG9mIHRoZSBuZWlnaGJvdXJob29kIGlzIGNvbnRyb2xsZWQgYnkgzrEgKHNldCBieSBzcGFuIG9yIGVucC50YXJnZXQpLiBGb3IgzrEgPCAxLCB0aGUgbmVpZ2hib3VyaG9vZCBpbmNsdWRlcyBwcm9wb3J0aW9uIM6xIG9mIHRoZSBwb2ludHMsIGFuZCB0aGVzZSBoYXZlIHRyaWN1YmljIHdlaWdodGluZyAocHJvcG9ydGlvbmFsIHRvICgxIC0gKGRpc3QvbWF4ZGlzdCleMyleMykuIEZvciDOsSA+IDEsIGFsbCBwb2ludHMgYXJlIHVzZWQsIHdpdGggdGhlIOKAmG1heGltdW0gZGlzdGFuY2XigJkgYXNzdW1lZCB0byBiZSDOsV4oMS9wKSB0aW1lcyB0aGUgYWN0dWFsIG1heGltdW0gZGlzdGFuY2UgZm9yIHAgZXhwbGFuYXRvcnkgdmFyaWFibGVzLgoKYGBge3IsIGNhY2hlPVRSVUV9CnBhcihtZnJvdz1jKDQsNiksIG1hcj1jKDAsMCwwLDApKQpmb3IoaCBpbiAxOjIwKXsKI2ggPC0gMwpkZW5zaXR5X3RyZW5kIDwtIGFsbF92ZWN0b3JzW3doaWNoKGFsbF92ZWN0b3JzWywxXSA9PSBuYW1lcyhwZXIub3JpZ2luKVtoXSksXQpvcmRlcmVkX2RlbnNpdHlfdHJlbmQgPC0gZGVuc2l0eV90cmVuZFtvcmRlcihkZW5zaXR5X3RyZW5kJHhfdmFsdWVzKSwgXQoKZ2FtbWVyIDwtIGxvZXNzKGFzLm51bWVyaWMob3JkZXJlZF9kZW5zaXR5X3RyZW5kWyw0XSkgfiBhcy5udW1lcmljKG9yZGVyZWRfZGVuc2l0eV90cmVuZFssMl0pLCBzcGFuID0gLjUpCnN1bW1hcnkoZ2FtbWVyKQpwcmVkaWN0X2dhbSA8LSBwcmVkaWN0KGdhbW1lciwgc2U9VFJVRSkKCnBsb3QoYXMubnVtZXJpYyhkZW5zaXR5X3RyZW5kWywyXSksIGFzLm51bWVyaWMoZGVuc2l0eV90cmVuZFssNF0pLCBjb2w9YWRqdXN0Y29sb3IoImNvcm5mbG93ZXJibHVlIiwgYWxwaGE9MC44KSwgIHhsYWI9InllYXIiLCB5bGFiPSJEZW5zaXR5IiwgeWxpbT1jKC0yLDIpLCB4bGltPWMoMCwyMSksIHR5cGU9Im4iLCB4YXh0PSJuIiwgeWF4dD0ibiIpCiNheGlzKDEsIGxhYmVsPSBzZXEoNCwyMSwgYnk9MSksIGF0PXJldihzZXEoMSwxOCwgYnk9MSkpKQoKcG9seWdvbih4PTIyLWMoU3RhcnRfb2ZfZWFybHlfd2luZG93LCBTdGFydF9vZl9lYXJseV93aW5kb3csIEVuZF9vZl9lYXJseV93aW5kb3dfc3RhcnRfb2ZfbGF0ZV93aW5kb3csIEVuZF9vZl9lYXJseV93aW5kb3dfc3RhcnRfb2ZfbGF0ZV93aW5kb3cpLCB5PWMoLTIsIDIsIDIsIC0yKSwgY29sPWFkanVzdGNvbG9yKCJsaW1lZ3JlZW4iLCBhbHBoYT0gMC4yKSwgYm9yZGVyPWFkanVzdGNvbG9yKCJsaW1lZ3JlZW4iLCBhbHBoYT0gMC45KSkKCnBvbHlnb24oeD0yMi1jKCBFbmRfb2ZfZWFybHlfd2luZG93X3N0YXJ0X29mX2xhdGVfd2luZG93LCBFbmRfb2ZfZWFybHlfd2luZG93X3N0YXJ0X29mX2xhdGVfd2luZG93LCBFbmRfb2ZfbGF0ZV93aW5kb3csIEVuZF9vZl9sYXRlX3dpbmRvdyksIHk9YygtMiwgMiwgMiwgLTIpLCBjb2w9YWRqdXN0Y29sb3IoImZpcmVicmljayIsIGFscGhhPSAwLjIpLCBib3JkZXI9YWRqdXN0Y29sb3IoImZpcmVicmljayIsIGFscGhhPSAwLjkpKQoKYWJsaW5lKGg9MCwgbHR5PTIsIGNvbD0iZ3JleSIpCgpsaW5lcyhvcmRlcmVkX2RlbnNpdHlfdHJlbmRbLDJdICxwcmVkaWN0X2dhbSRmaXQsICBjb2w9ImNvcm5mbG93ZXJibHVlIikKbGluZXMob3JkZXJlZF9kZW5zaXR5X3RyZW5kWywyXSAsICBwcmVkaWN0X2dhbSRmaXQgKyBwcmVkaWN0X2dhbSRzZS5maXQsICBjb2w9ImdyZXkiLCBsdHk9MSkKbGluZXMob3JkZXJlZF9kZW5zaXR5X3RyZW5kWywyXSAsICBwcmVkaWN0X2dhbSRmaXQgLSBwcmVkaWN0X2dhbSRzZS5maXQsICBjb2w9ImdyZXkiLCBsdHk9MSkKCgptdGV4dChhcy5jaGFyYWN0ZXIob3JkZXJlZF9kZW5zaXR5X3RyZW5kWzEsMV0pLCAzLCBsaW5lPS0xLCBjZXg9MC41KQp9CgpgYGAKCgoKCmBgYHtyLCBjYWNoZT1UUlVFfQpwYXIobWZyb3c9YygyLDUpLCBtYXI9YygwLDAsMCwwKSkKZm9yKGogaW4gc2VxKDAuMiwgMiwgYnk9LjIpKXsKCnBsb3QoYXMubnVtZXJpYyhkZW5zaXR5X3RyZW5kWywyXSksIGFzLm51bWVyaWMoZGVuc2l0eV90cmVuZFssNF0pLCBjb2w9YWRqdXN0Y29sb3IoImNvcm5mbG93ZXJibHVlIiwgYWxwaGE9MC44KSwgIHhsYWI9InllYXIiLCB5bGFiPSJEZW5zaXR5IiwgeWxpbT1jKC0zLDMpLCB4bGltPWMoMCwyMSksIHR5cGU9Im4iLCB4YXh0PSJuIikKYXhpcygxLCBsYWJlbD0gc2VxKDQsMjEsIGJ5PTEpLCBhdD1yZXYoc2VxKDEsMTgsIGJ5PTEpKSkKCgpwb2x5Z29uKHg9MjItYyhTdGFydF9vZl9lYXJseV93aW5kb3csIFN0YXJ0X29mX2Vhcmx5X3dpbmRvdywgRW5kX29mX2Vhcmx5X3dpbmRvd19zdGFydF9vZl9sYXRlX3dpbmRvdywgRW5kX29mX2Vhcmx5X3dpbmRvd19zdGFydF9vZl9sYXRlX3dpbmRvdyksIHk9YygtMiwgMiwgMiwgLTIpLCBjb2w9YWRqdXN0Y29sb3IoImxpbWVncmVlbiIsIGFscGhhPSAwLjIpLCBib3JkZXI9YWRqdXN0Y29sb3IoImxpbWVncmVlbiIsIGFscGhhPSAwLjkpKQoKcG9seWdvbih4PTIyLWMoIEVuZF9vZl9lYXJseV93aW5kb3dfc3RhcnRfb2ZfbGF0ZV93aW5kb3csIEVuZF9vZl9lYXJseV93aW5kb3dfc3RhcnRfb2ZfbGF0ZV93aW5kb3csIEVuZF9vZl9sYXRlX3dpbmRvdywgRW5kX29mX2xhdGVfd2luZG93KSwgeT1jKC0yLCAyLCAyLCAtMiksIGNvbD1hZGp1c3Rjb2xvcigiZmlyZWJyaWNrIiwgYWxwaGE9IDAuMiksIGJvcmRlcj1hZGp1c3Rjb2xvcigiZmlyZWJyaWNrIiwgYWxwaGE9IDAuOSkpCgoKZm9yKGggaW4gMToxOCl7CiNoIDwtIDMKZGVuc2l0eV90cmVuZCA8LSBhbGxfdmVjdG9yc1t3aGljaChhbGxfdmVjdG9yc1ssMV0gPT0gbGV2ZWxzKGFsbF92ZWN0b3JzWywxXSlbaF0pLF0Kb3JkZXJlZF9kZW5zaXR5X3RyZW5kIDwtIGRlbnNpdHlfdHJlbmRbb3JkZXIoZGVuc2l0eV90cmVuZCR4X3ZhbHVlcyksIF0KCmdhbW1lciA8LSBsb2Vzcyhhcy5udW1lcmljKG9yZGVyZWRfZGVuc2l0eV90cmVuZFssNF0pIH4gYXMubnVtZXJpYyhvcmRlcmVkX2RlbnNpdHlfdHJlbmRbLDJdKSwgc3BhbiA9IGopCnN1bW1hcnkoZ2FtbWVyKQpwcmVkaWN0X2dhbSA8LSBwcmVkaWN0KGdhbW1lciwgc2U9VFJVRSkKCmFibGluZShoPTAsIGx0eT0yLCBjb2w9ImdyZXkiKQoKbGluZXMob3JkZXJlZF9kZW5zaXR5X3RyZW5kWywyXSAscHJlZGljdF9nYW0kZml0LCAgY29sPSJjb3JuZmxvd2VyYmx1ZSIpCgp9Cm10ZXh0KHBhc3RlKCJzcGFuID0gIiwgaiksIDMsIGxpbmU9LTMsIGNleD0wLjUpCn0KYGBgCgoKCgoKCgoKCgoKCgpgYGB7ciwgY2FjaGU9VFJVRX0KIyBuZWVkIHRvIGFkZCBhIGdsb2JhbCBtZWFuLCBhbiBldmVyeXRoaW5nIGJ1dCB0aGUgb3JpZ2lucyBtZWFuLCBhbmQgYSBidWZmZXIgYXJvdW5kIHRoZSBvcmlnaW5zIG1lYW4uIAojIEZ1bmN0aW9uIHN0YW5kYXJkaXphdGlvbgpzdGQgPC0gZnVuY3Rpb24oeCkgewogIGIgPC0gKHggLSBtaW4oeCkpIC8gKG1heCh4KSAtIG1pbih4KSkKICByZXR1cm4ocmV2KGIpKQp9CgoKZGlmZl9kZiA8LSBmdW5jdGlvbihoKXsgCiMgQ2FsY3VsYXRpbmcgbWVhbiBhbmQgCmdsb2JhbC5tZWFucyA8LSBnbG9iYWwuU0QgPC0gbGlzdCgpCgpmb3IgKGogaW4gMTpsZW5ndGgocGVyLm9yaWdpbikpIHsKICAjcHJpbnQoaikKICBvcmlnaW5JIDwtIFBvcFtwZXIub3JpZ2luW1tqXV1bLCAxXSwgXQogIHRpbWUgPC0gMjE6NAogIG9yaWdpbkkgPC0gbmEuZXhjbHVkZShvcmlnaW5JKQogIGIgPC0gYXBwbHkob3JpZ2luSSwgMSwgc3RkKQogIG5KIDwtIG5yb3cob3JpZ2luSSkKICBwcmVkaWN0aW9ucyA8LSBtYXRyaXgobnJvdyA9IG5KLCBuY29sID0gbGVuZ3RoKHRpbWUpKQogIGNvbG5hbWVzKHByZWRpY3Rpb25zKSA8LSBhcy5jaGFyYWN0ZXIodGltZSkKICBmb3IoaSBpbiAxOm5KKSB7CiAgICAKICAgICMgTmVlZCB0byBzaG93IGEgZ3JhZGllbnQgb2YgdGhlc2UgZGYgdmFsdWVzLiAKICAgIG1vZGVsIDwtIGdhbShiWywgaV0gfiBzKHRpbWUsIGRmID0gaCkpCiAgICBjb2wgPC0gc2FtcGxlKHJhaW5ib3coMTAwKSwgMSkKICAgIHByZWRpY3Rpb25zW2ksIF0gPC0gcHJlZGljdChtb2RlbCkKICAgICNwbG90KGJbLCBpXSB+IHRpbWUpCiAgICAjbGluZXMocHJlZGljdGlvbnNbaSwgXSB+IHRpbWUpCiAgfQogIGdsb2JhbC5tZWFuc1tbal1dIDwtIGFwcGx5KHByZWRpY3Rpb25zLCAyLCBtZWFuKSAKICBnbG9iYWwuU0RbW2pdXSA8LSBhcHBseShwcmVkaWN0aW9ucywgMiwgc2QpCn0KCgpuYW1lcyhnbG9iYWwubWVhbnMpIDwtIG5hbWVzKHBlci5vcmlnaW4pCm5hbWVzKGdsb2JhbC5TRCkgPC0gbmFtZXMocGVyLm9yaWdpbikKCnJldHVybihsaXN0KGdsb2JhbC5tZWFucywgZ2xvYmFsLlNEKSkKfQoKCmBgYAoKCgpgYGB7cn0KbWVhbnNfbWF0cml4IDwtIG1hdHJpeChyZXAoTkEsMTkqMjApLCAyMCwgMTkpCmNvbG5hbWVzKG1lYW5zX21hdHJpeCkgPC0gYygib3JpZ2luIiwgcmV2KHNlcSg0LCAyMSwgYnk9MSkpKQptZWFuc19tYXRyaXhbLDFdIDwtIG5hbWVzKGdsb2JhbC5tZWFucykKZm9yKGkgaW4gMToyMCl7Cm1lYW5zX21hdHJpeFtpLDI6MTldIDwtIGdsb2JhbC5tZWFuc1tbaV1dCn0KYGBgCgoKCmBgYHtyfQojZ2xvYmFsLlNECgpTRF9tYXRyaXggPC0gbWF0cml4KHJlcChOQSwxOSoyMCksIDIwLCAxOSkKY29sbmFtZXMoU0RfbWF0cml4KSA8LSBjKCJvcmlnaW4iLCByZXYoc2VxKDQsIDIxLCBieT0xKSkpClNEX21hdHJpeFssMV0gPC0gbmFtZXMoZ2xvYmFsLlNEKQpmb3IoaSBpbiAxOjIwKXsKU0RfbWF0cml4W2ksMjoxOV0gPC0gZ2xvYmFsLlNEW1tpXV0KfQpgYGAKCgoKCgoKYGBge3IsIGNhY2hlPVRSVUV9CnBhcihtZnJvdz1jKDQsNSksIG1hcj1jKDAsMCwwLDApKQoKZm9yXzMgPC0gZGlmZl9kZigxKQpnbG9iYWwubWVhbnMgPC0gZm9yXzNbWzFdXQpnbG9iYWwuU0QgPC0gZm9yXzNbWzJdXQoKZm9yKGkgaW4gMToyMCl7CiAgCgpwbG90KDQ6MjEsIGdsb2JhbC5tZWFuc1tbaV1dLCBjb2w9YWRqdXN0Y29sb3IoImNvcm5mbG93ZXJibHVlIiwgYWxwaGE9MC44KSwgIHhsYWI9InllYXIiLCB5bGFiPSJEZW5zaXR5IiwgeGF4dD0ibiIsIHR5cGU9Im4iLCB5bGltPWMoMCwxKSwgeGxpbT1jKDAsMjIpLCB4YXh0PSJuIiwgeWF4dD0ibiIpCgpwb2x5Z29uKHg9MjItYyhTdGFydF9vZl9lYXJseV93aW5kb3csIFN0YXJ0X29mX2Vhcmx5X3dpbmRvdywgRW5kX29mX2Vhcmx5X3dpbmRvd19zdGFydF9vZl9sYXRlX3dpbmRvdywgRW5kX29mX2Vhcmx5X3dpbmRvd19zdGFydF9vZl9sYXRlX3dpbmRvdykgLCB5PWMoLTEsIDIsIDIsIC0xKSwgY29sPWFkanVzdGNvbG9yKCJsaW1lZ3JlZW4iLCBhbHBoYT0gMC4yKSwgYm9yZGVyPWFkanVzdGNvbG9yKCJsaW1lZ3JlZW4iLCBhbHBoYT0gMC45KSkKCnBvbHlnb24oeD0gMjItYyggRW5kX29mX2Vhcmx5X3dpbmRvd19zdGFydF9vZl9sYXRlX3dpbmRvdywgRW5kX29mX2Vhcmx5X3dpbmRvd19zdGFydF9vZl9sYXRlX3dpbmRvdywgRW5kX29mX2xhdGVfd2luZG93LCBFbmRfb2ZfbGF0ZV93aW5kb3cpICwgeT1jKC0xLCAyLCAyLCAtMSksIGNvbD1hZGp1c3Rjb2xvcigiZmlyZWJyaWNrIiwgYWxwaGE9IDAuMiksIGJvcmRlcj1hZGp1c3Rjb2xvcigiZmlyZWJyaWNrIiwgYWxwaGE9IDAuOSkpICAKICAKcG9seWdvbihjKDQ6MjEsMjE6NCkgLTMgLCBjKGdsb2JhbC5tZWFuc1tbaV1dICsgYWJzKGdsb2JhbC5TRFtbaV1dKSwgcmV2KGdsb2JhbC5tZWFuc1tbaV1dIC0gYWJzKGdsb2JhbC5TRFtbaV1dKSkpLCBjb2w9ImNvcm5mbG93ZXJibHVlIikKbGluZXMoZ2xvYmFsLm1lYW5zW1tpXV0pCgojYXhpcygxLCBhdD1zZXEoMSwxOCwgYnk9MSksIGxhYmVsPXJldihzZXEoNCwgMjEsIGJ5PTEpKSkKbXRleHQobmFtZXMoZ2xvYmFsLm1lYW5zKVtpXSwgMywgbGluZT0tMSwgY2V4PS41LCBhZGo9LjMpCn0KYGBgCkdBTSBtb2RlbCB1c2luZyBvbmUgZGVncmVlIG9mIGZyZWVkb20KCmBgYHtyLCBjYWNoZT1UUlVFfQpwYXIobWZyb3c9Yyg0LDUpLCBtYXI9YygwLDAsMCwwKSkKCmZvcl8zIDwtIGRpZmZfZGYoMikKZ2xvYmFsLm1lYW5zIDwtIGZvcl8zW1sxXV0KZ2xvYmFsLlNEIDwtIGZvcl8zW1syXV0KCmZvcihpIGluIDE6MjApewpwbG90KDQ6MjEsIGdsb2JhbC5tZWFuc1tbaV1dLCBjb2w9YWRqdXN0Y29sb3IoImNvcm5mbG93ZXJibHVlIiwgYWxwaGE9MC44KSwgIHhsYWI9InllYXIiLCB5bGFiPSJEZW5zaXR5IiwgeGF4dD0ibiIsIHR5cGU9Im4iLCB5bGltPWMoMCwxKSwgeGxpbT1jKDAsMjIpLCB4YXh0PSJuIiwgeWF4dD0ibiIpCgpwb2x5Z29uKHg9MjItYyhTdGFydF9vZl9lYXJseV93aW5kb3csIFN0YXJ0X29mX2Vhcmx5X3dpbmRvdywgRW5kX29mX2Vhcmx5X3dpbmRvd19zdGFydF9vZl9sYXRlX3dpbmRvdywgRW5kX29mX2Vhcmx5X3dpbmRvd19zdGFydF9vZl9sYXRlX3dpbmRvdykgLCB5PWMoLTEsIDIsIDIsIC0xKSwgY29sPWFkanVzdGNvbG9yKCJsaW1lZ3JlZW4iLCBhbHBoYT0gMC4yKSwgYm9yZGVyPWFkanVzdGNvbG9yKCJsaW1lZ3JlZW4iLCBhbHBoYT0gMC45KSkKCnBvbHlnb24oeD0gMjItYyggRW5kX29mX2Vhcmx5X3dpbmRvd19zdGFydF9vZl9sYXRlX3dpbmRvdywgRW5kX29mX2Vhcmx5X3dpbmRvd19zdGFydF9vZl9sYXRlX3dpbmRvdywgRW5kX29mX2xhdGVfd2luZG93LCBFbmRfb2ZfbGF0ZV93aW5kb3cpICwgeT1jKC0xLCAyLCAyLCAtMSksIGNvbD1hZGp1c3Rjb2xvcigiZmlyZWJyaWNrIiwgYWxwaGE9IDAuMiksIGJvcmRlcj1hZGp1c3Rjb2xvcigiZmlyZWJyaWNrIiwgYWxwaGE9IDAuOSkpCgpwb2x5Z29uKGMoNDoyMSwyMTo0KSAtMyAsIGMoZ2xvYmFsLm1lYW5zW1tpXV0gKyBhYnMoZ2xvYmFsLlNEW1tpXV0pLCByZXYoZ2xvYmFsLm1lYW5zW1tpXV0gLSBhYnMoZ2xvYmFsLlNEW1tpXV0pKSksIGNvbD0iY29ybmZsb3dlcmJsdWUiKQpsaW5lcyhnbG9iYWwubWVhbnNbW2ldXSkKCiNheGlzKDEsIGF0PXNlcSgxLDE4LCBieT0xKSwgbGFiZWw9cmV2KHNlcSg0LCAyMSwgYnk9MSkpKQptdGV4dChuYW1lcyhnbG9iYWwubWVhbnMpW2ldLCAzLCBsaW5lPS0xLCBjZXg9LjUsIGFkaj0uMykKfQpgYGAKR0FNIG1vZGVsIHVzaW5nIHR3byBkZWdyZWUgb2YgZnJlZWRvbQoKYGBge3IsIGNhY2hlPVRSVUV9CnBhcihtZnJvdz1jKDQsNSksIG1hcj1jKDAsMCwwLDApKQoKZm9yXzMgPC0gZGlmZl9kZigzKQpnbG9iYWwubWVhbnMgPC0gZm9yXzNbWzFdXQpnbG9iYWwuU0QgPC0gZm9yXzNbWzJdXQoKZm9yKGkgaW4gMToyMCl7CnBsb3QoNDoyMSwgZ2xvYmFsLm1lYW5zW1tpXV0sIGNvbD1hZGp1c3Rjb2xvcigiY29ybmZsb3dlcmJsdWUiLCBhbHBoYT0wLjgpLCAgeGxhYj0ieWVhciIsIHlsYWI9IkRlbnNpdHkiLCB4YXh0PSJuIiwgdHlwZT0ibiIsIHlsaW09YygwLDEpLCB4bGltPWMoMCwyMiksIHhheHQ9Im4iLCB5YXh0PSJuIikKCnBvbHlnb24oeD0yMi1jKFN0YXJ0X29mX2Vhcmx5X3dpbmRvdywgU3RhcnRfb2ZfZWFybHlfd2luZG93LCBFbmRfb2ZfZWFybHlfd2luZG93X3N0YXJ0X29mX2xhdGVfd2luZG93LCBFbmRfb2ZfZWFybHlfd2luZG93X3N0YXJ0X29mX2xhdGVfd2luZG93KSAsIHk9YygtMSwgMiwgMiwgLTEpLCBjb2w9YWRqdXN0Y29sb3IoImxpbWVncmVlbiIsIGFscGhhPSAwLjIpLCBib3JkZXI9YWRqdXN0Y29sb3IoImxpbWVncmVlbiIsIGFscGhhPSAwLjkpKQoKcG9seWdvbih4PSAyMi1jKCBFbmRfb2ZfZWFybHlfd2luZG93X3N0YXJ0X29mX2xhdGVfd2luZG93LCBFbmRfb2ZfZWFybHlfd2luZG93X3N0YXJ0X29mX2xhdGVfd2luZG93LCBFbmRfb2ZfbGF0ZV93aW5kb3csIEVuZF9vZl9sYXRlX3dpbmRvdykgLCB5PWMoLTEsIDIsIDIsIC0xKSwgY29sPWFkanVzdGNvbG9yKCJmaXJlYnJpY2siLCBhbHBoYT0gMC4yKSwgYm9yZGVyPWFkanVzdGNvbG9yKCJmaXJlYnJpY2siLCBhbHBoYT0gMC45KSkKCnBvbHlnb24oYyg0OjIxLDIxOjQpIC0zICwgYyhnbG9iYWwubWVhbnNbW2ldXSArIGFicyhnbG9iYWwuU0RbW2ldXSksIHJldihnbG9iYWwubWVhbnNbW2ldXSAtIGFicyhnbG9iYWwuU0RbW2ldXSkpKSwgY29sPSJjb3JuZmxvd2VyYmx1ZSIpCmxpbmVzKGdsb2JhbC5tZWFuc1tbaV1dKQoKI2F4aXMoMSwgYXQ9c2VxKDEsMTgsIGJ5PTEpLCBsYWJlbD1yZXYoc2VxKDQsIDIxLCBieT0xKSkpCm10ZXh0KG5hbWVzKGdsb2JhbC5tZWFucylbaV0sIDMsIGxpbmU9LTEsIGNleD0uNSwgYWRqPS4zKQp9CmBgYApHQU0gbW9kZWwgdXNpbmcgdGhyZWUgZGVncmVlIG9mIGZyZWVkb20KCgoKYGBge3J9CnBhcihtZnJvdz1jKDQsNSksIG1hcj1jKDAsMCwwLDApKQoKZm9yXzMgPC0gZGlmZl9kZigxNykKZ2xvYmFsLm1lYW5zIDwtIGZvcl8zW1sxXV0KZ2xvYmFsLlNEIDwtIGZvcl8zW1syXV0KCmZvcihpIGluIDE6MjApewpwbG90KDQ6MjEsIGdsb2JhbC5tZWFuc1tbaV1dLCBjb2w9YWRqdXN0Y29sb3IoImNvcm5mbG93ZXJibHVlIiwgYWxwaGE9MC44KSwgIHhsYWI9InllYXIiLCB5bGFiPSJEZW5zaXR5IiwgeGF4dD0ibiIsIHR5cGU9Im4iLCB5bGltPWMoMCwxKSwgeGxpbT1jKDAsMjIpLCB4YXh0PSJuIiwgeWF4dD0ibiIpCgpwb2x5Z29uKHg9MjItYyhTdGFydF9vZl9lYXJseV93aW5kb3csIFN0YXJ0X29mX2Vhcmx5X3dpbmRvdywgRW5kX29mX2Vhcmx5X3dpbmRvd19zdGFydF9vZl9sYXRlX3dpbmRvdywgRW5kX29mX2Vhcmx5X3dpbmRvd19zdGFydF9vZl9sYXRlX3dpbmRvdykgLCB5PWMoLTEsIDIsIDIsIC0xKSwgY29sPWFkanVzdGNvbG9yKCJsaW1lZ3JlZW4iLCBhbHBoYT0gMC4yKSwgYm9yZGVyPWFkanVzdGNvbG9yKCJsaW1lZ3JlZW4iLCBhbHBoYT0gMC45KSkKCnBvbHlnb24oeD0gMjItYyggRW5kX29mX2Vhcmx5X3dpbmRvd19zdGFydF9vZl9sYXRlX3dpbmRvdywgRW5kX29mX2Vhcmx5X3dpbmRvd19zdGFydF9vZl9sYXRlX3dpbmRvdywgRW5kX29mX2xhdGVfd2luZG93LCBFbmRfb2ZfbGF0ZV93aW5kb3cpICwgeT1jKC0xLCAyLCAyLCAtMSksIGNvbD1hZGp1c3Rjb2xvcigiZmlyZWJyaWNrIiwgYWxwaGE9IDAuMiksIGJvcmRlcj1hZGp1c3Rjb2xvcigiZmlyZWJyaWNrIiwgYWxwaGE9IDAuOSkpICAgCiAgCnBvbHlnb24oYyg0OjIxLDIxOjQpIC0zICwgYyhnbG9iYWwubWVhbnNbW2ldXSArIGFicyhnbG9iYWwuU0RbW2ldXSksIHJldihnbG9iYWwubWVhbnNbW2ldXSAtIGFicyhnbG9iYWwuU0RbW2ldXSkpKSwgY29sPSJjb3JuZmxvd2VyYmx1ZSIpCmxpbmVzKGdsb2JhbC5tZWFuc1tbaV1dKQoKI2F4aXMoMSwgYXQ9c2VxKDEsMTgsIGJ5PTEpLCBsYWJlbD1yZXYoc2VxKDQsIDIxLCBieT0xKSkpCm10ZXh0KG5hbWVzKGdsb2JhbC5tZWFucylbaV0sIDMsIGxpbmU9LTEsIGNleD0uNSwgYWRqPS4zKQp9CmBgYApHQU0gbW9kZWwgdXNpbmcgMTcgZGVncmVlIG9mIGZyZWVkb20KCgoKIyMjIyBQcm9kdWN0aXZpdHkKYGBge3J9CiMgTG9hZCBwYXRyaWNrcyBwcm9kdWN0aXZpdHkgUENBIGRhdGEKbG9hZCgnUHJvZHVjdGl2aXR5X0FMTC5SREFUQScpCgojIExvYWQgb3JpZ2luIHNoYXBlZmlsZXMKb3JpZ2lucyA8LSByZWFkU2hhcGVQb2x5KCdPcmlnaW5zX3VwZGF0ZWQuc2hwJykKCm9yaWdpbi50aW1lLnJlZ2lvbiA8LSBjKDIsIDIsIDEsIDEsIDEsIDIsIDIsIDEsIDIsIDIsIAogICAgICAgICAgICAgICAgICAgICAgICAyLCAyLCAxLCAyLCAyLCAyLCAyLCAyLCAyLCAyKSAjIDEgPSBlYXJseTsgMiA9IG1pZGRsZQoKCiMgRXh0cmFjdCB0aGUgZGF0YQpwcm9kLm9yaWdpbiA8LSBleHRyYWN0KFByb2R1Y3Rpdml0eS5BTEwsIG9yaWdpbnMpCiMgTWVhbiBhbmQgU0QgcGVyIHJlZ2lvbgptZWFucyA8LSBsYXBwbHkocHJvZC5vcmlnaW4sIGNvbE1lYW5zLCBuYS5ybSA9IFRSVUUpCnNkcyA8LSBsYXBwbHkocHJvZC5vcmlnaW4sIHNkLCBuYS5ybSA9IFRSVUUpCm5hbWVzKG1lYW5zKSA8LSBvcmlnaW5zQGRhdGEkQ09OVElORU5UCnltYXggPC0gbWF4KHVubGlzdChtZWFucykpCnltaW4gPC0gbWluKHVubGlzdChtZWFucykpCnRpbWUgPC0gNDoyMQoKIyBQbG90CiNwZGYoInByb2R1Y3Rpdml0eS5wZGYiLCAyMCwgMzApIApwYXIobWZyb3cgPSBjKDUsIDQpLCBtYXIgPSBjKDIsIDIsIDIsIDApKQpmb3IgKGkgaW4gMTpsZW5ndGgobWVhbnMpKSB7CiAgcGxvdCh5ID0gbWVhbnNbW2ldXSwgeCA9IHRpbWUsIHhsaW0gPSBjKDIxLCA0KSwgeWxpbSA9IGMoeW1pbiwgeW1heCksCiAgICAgICBtYWluID0gbmFtZXMobWVhbnMpW2ldLCBjZXgubWFpbiA9IDEsIGNleC5sYWIgPSAxLCBjZXguYXhpcyA9IDEsCiAgICAgICB5bGFiID0gIlByb2R1Y3Rpdml0eSAoUENBIGF4aXMpIiwgeGxhYiA9ICJUaG91c2FuZCBvZiB5ZWFycyBhZ28gKGspIiwKICAgICAgIHBjaCA9IDIwLCBsd2QgPSAxLCB0eXBlID0gImwiLCAKICAgICAgIGNvbCA9IGMoInB1cnBsZSIsICJncmVlbiIpW29yaWdpbi50aW1lLnJlZ2lvbltpXV0pCiAgdXAgPC0gc2RzW1tpXV0gKyBtZWFuc1tbaV1dCiAgZG93biA8LSAgbWVhbnNbW2ldXSAtIHNkc1tbaV1dCiAgbGluZXModXAgfiB0aW1lLCBsdHkgPSAyKQogIGxpbmVzKGRvd24gfiB0aW1lLCBsdHkgPSAyKQogIAp9CiNkZXYub2ZmKCkKYGBgCgoKCgojI0NvbXBhcmUgcmF0ZXMgYmV0d2VlbiBvcmlnaW5zIGFuZCBub3Qtb3JpZ2lucwoKCgpgYGB7cn0KCmhpc3QoYXMubnVtZXJpYyhub3Rfb3JpZ2luc1ssNF0pLCBjb2w9YWRqdXN0Y29sb3IoImNvcm5mbG93ZXJibHVlIiwgYWxwaGE9LjUpLCAgYnJlYWtzPTEwMCkKaGlzdChhcy5udW1lcmljKG9yaWdpbnNbLDRdKSwgYWRkPVRSVUUsIGNvbD1hZGp1c3Rjb2xvcigibGltZWdyZWVuIiwgYWxwaGE9LjUpLCBicmVha3M9MTAwKQpgYGAKCgojI0NvbXBhcmUgcmF0ZXMgYmV0d2VlbiBkaWZmZXJlbnQgdGltZSBwZXJpb2RzCgoKIyNTZXR1cCBmaW5hbCBmaWd1cmUKIyMjIyBGcmFtZSBpbiB0aGUgbGF5b3V0CmBgYHtyfQphIDwtIGxheW91dChtYXRyaXgoYygKCTEsIDEsIDEsIDEsIDEsIDEsIDEsIDEsCgkzLAk2LCA3LCA4LCA5LCAxMCwgMTEsCTQsIAoJMywJNSwgNSwgNSwgNSwgNSwgNSwgCTQsIAoJMywgCTEyLCAxMywgMTQsIDE1LCAxNiwgMTcsCTQsCgkyLCAyLCAyLCAyLCAyLCAyLCAyLCAyCgkpLCA1LCA4LCBieXJvdz1UUlVFKSwgd2lkdGg9YygxLCAxLCAxLCAxLCAxLCAxLCAxLCAxKSwgaGVpZ2h0PWMoMC41LCAxLCAxLjUsIDEsIDAuNSkpCmxheW91dC5zaG93KGEpCmBgYAoKIyMjIyBNYWtlIGJsYW5rIHRlbXBsYXRlIHBsb3RzCmBgYHtyfQpmcmFtZXBsb3QgPC0gZnVuY3Rpb24oKXsKCXBsb3QoMjE6MCxyZXAoMCwgMjIpLCB4bGltPWMoMjEsMCksIHlsaW09YygtMywgMi4yNSksIHR5cGU9Im4iLCB4YXh0PSJuIiwgeWF4dD0ibiIsIHhsYWI9IiIsIHlsYWI9IiIpCn0KCmZyYW1lcGxvdF9ib3R0b20gPC0gZnVuY3Rpb24oKXsKCXBsb3QoMjE6MCxyZXAoMCwgMjIpLCB4bGltPWMoMjEsMCksIHlsaW09YygtMy4yNSwgMiksIHR5cGU9Im4iLCB4YXh0PSJuIiwgeWF4dD0ibiIsIHhsYWI9IiIsIHlsYWI9IiIpCn0KCmZyYW1lcGxvdF9mbGV4IDwtIGZ1bmN0aW9uKG1pbiwgbWF4KXsKCXBsb3QoMjE6MCxyZXAoMCwgMjIpLCB4bGltPWMoMjEsMCksIHlsaW09YyhtaW4sIG1heCksIHR5cGU9Im4iLCB4YXh0PSJuIiwgeWF4dD0ibiIsIHhsYWI9IiIsIHlsYWI9IiIpCn0KCmZyYW1lcGxvdF9ib3R0b21fZmxleCA8LSBmdW5jdGlvbihtaW4sIG1heCl7CglwbG90KDIxOjAscmVwKDAsIDIyKSwgeGxpbT1jKDIxLDApLCB5bGltPWMobWluLCBtYXgpLCB0eXBlPSJuIiwgeGF4dD0ibiIsIHlheHQ9Im4iLCB4bGFiPSIiLCB5bGFiPSIiKQp9CgpgYGAKCgoKIyNUcmVuZCB0aHJvdWdoIHRpbWUgcGFuZWwKCiMjIyMgU2V0dXAgdGhlIHBsb3QgdGVtcGxhdGUgZm9yIHNtYWxsIHBhbmVsIHBsb3RzICgjNi0xNyBvbiBsYXlvdXQgcGFuZWwpCgpgYGB7cn0KI3Bsb3QgdHlwZSAxCmxpbmVzX3NjYWxlZF9sb2Vzc19ub196b29tIDwtIGZ1bmN0aW9uKGFsbF92ZWN0b3JzLCBsb2NhdGlvbil7CiAgI2ggPC0gMwoKICAKb3JpZ2lucyA8LSBzdWJzZXQoYWxsX3ZlY3RvcnMsIGxvY2F0aW9uX25hbWUgIT0gIm5vdF9vcmlnaW5zIikKZGVuc2l0eV90cmVuZCA8LSBvcmlnaW5zW3doaWNoKG9yaWdpbnNbLDFdID09IGxvY2F0aW9uICksXQoKI3Bsb3QoYXMubnVtZXJpYyhkZW5zaXR5X3RyZW5kWywyXSksIGFzLm51bWVyaWMoZGVuc2l0eV90cmVuZFssNF0pLCBjb2w9YWRqdXN0Y29sb3IoImNvcm5mbG93ZXJibHVlIiwgYWxwaGE9MC44KSwgIHhsYWI9InllYXIiLCB5bGFiPSJEZW5zaXR5IiwgeWxpbT1jKC0yLDIpLCB4bGltPWMoNCwyMSksIHR5cGU9Im4iKQpnIDwtIDY3NDA0CmZvcihnIGluIGxldmVscyhhcy5mYWN0b3IoYXMuY2hhcmFjdGVyKGRlbnNpdHlfdHJlbmRbLDVdKSkpKXsKc3ViYmVkIDwtIHN1YnNldChkZW5zaXR5X3RyZW5kLCBjZWxsX0lEID09IGFzLm51bWVyaWMoZykpCnN1YmJlZCA8LSBhcy5kYXRhLmZyYW1lKHN1YmJlZCkKY2xhc3Moc3ViYmVkWywyXSkgPC0gIm51bWVyaWMiCm9yZGVyZWRfc3ViYmVkIDwtIHN1YmJlZFtvcmRlcihzdWJiZWQkeF92YWx1ZXMpLCBdCmxpbmVzKDMrcmV2KG9yZGVyZWRfc3ViYmVkJHhfdmFsdWVzKSwgYXMubnVtZXJpYyhvcmRlcmVkX3N1YmJlZFssNF0pLCBsd2Q9LjMsIGNvbD1hZGp1c3Rjb2xvcigibGltZWdyZWVuIiwgMC4zKSkKfQoKCgpvcmRlcmVkX2RlbnNpdHlfdHJlbmQgPC0gZGVuc2l0eV90cmVuZFtvcmRlcihkZW5zaXR5X3RyZW5kJHhfdmFsdWVzKSwgXQoKZ2FtbWVyIDwtIGxvZXNzKGFzLm51bWVyaWMob3JkZXJlZF9kZW5zaXR5X3RyZW5kWyw0XSkgfiBhcy5udW1lcmljKG9yZGVyZWRfZGVuc2l0eV90cmVuZFssMl0pLCBzcGFuID0gMSkKc3VtbWFyeShnYW1tZXIpCnByZWRpY3RfZ2FtIDwtIHByZWRpY3QoZ2FtbWVyLCBzZT1UUlVFKQoKCgphYmxpbmUoaD0wLCBsdHk9MiwgY29sPSJncmV5IikKCgojbGluZXMocmV2KG9yZGVyZWRfZGVuc2l0eV90cmVuZFssMl0pICwgIHByZWRpY3RfZ2FtJGZpdCArIHByZWRpY3RfZ2FtJHNlLmZpdCwgIGNvbD0ib3JhbmdlIiwgbHR5PTEsIGx3ZD0xKQojbGluZXMocmV2KG9yZGVyZWRfZGVuc2l0eV90cmVuZFssMl0pICwgIHByZWRpY3RfZ2FtJGZpdCAtIHByZWRpY3RfZ2FtJHNlLmZpdCwgIGNvbD0ib3JhbmdlIiwgbHR5PTEsIGx3ZD0xKQoKCmxpbmVzKDMrcmV2KGFzLm51bWVyaWMob3JkZXJlZF9kZW5zaXR5X3RyZW5kWywyXSkpICxwcmVkaWN0X2dhbSRmaXQsICBjb2w9ImZpcmVicmljayIsIGx3ZD0xKQoKCiAgCn0KCgpmcmFtZXBsb3QoKQpsaW5lc19zY2FsZWRfbG9lc3Nfbm9fem9vbShhbGxfdmVjdG9ycywgIkZlcnRpbGVfQ3Jlc2MiKQpheGlzKDIpCmBgYAoKCgoKYGBge3J9CiNwbG90IHR5cGUgMgpsb2NhdGlvbiA8LSAiRmVydGlsZV9DcmVzYyIKCmxpbmVzX25vdF9zY2FsZWRfbG9lc3Nfbm9fem9vbSA8LSBmdW5jdGlvbihhbGxfdmVjdG9ycywgbG9jYXRpb24pewogICNwbG90IHR5cGUgMQoKb3JpZ2lucyA8LSBzdWJzZXQoYWxsX3ZlY3RvcnMsIGxvY2F0aW9uX25hbWUgIT0gIm5vdF9vcmlnaW5zIikKZGVuc2l0eV90cmVuZCA8LSBvcmlnaW5zW3doaWNoKG9yaWdpbnNbLDFdID09IGxvY2F0aW9uICksXQoKI3Bsb3QoYXMubnVtZXJpYyhkZW5zaXR5X3RyZW5kWywyXSksIGFzLm51bWVyaWMoZGVuc2l0eV90cmVuZFssNF0pLCBjb2w9YWRqdXN0Y29sb3IoImNvcm5mbG93ZXJibHVlIiwgYWxwaGE9MC44KSwgIHhsYWI9InllYXIiLCB5bGFiPSJEZW5zaXR5IiwgeWxpbT1jKC0yLDIpLCB4bGltPWMoNCwyMSksIHR5cGU9Im4iKQpnIDwtIDY3NDA0CmZvcihnIGluIGxldmVscyhhcy5mYWN0b3IoYXMuY2hhcmFjdGVyKGRlbnNpdHlfdHJlbmRbLDVdKSkpKXsKc3ViYmVkIDwtIHN1YnNldChkZW5zaXR5X3RyZW5kLCBjZWxsX0lEID09IGFzLm51bWVyaWMoZykpCnN1YmJlZCA8LSBhcy5kYXRhLmZyYW1lKHN1YmJlZCkKY2xhc3Moc3ViYmVkWywyXSkgPC0gIm51bWVyaWMiCm9yZGVyZWRfc3ViYmVkIDwtIHN1YmJlZFtvcmRlcihzdWJiZWQkeF92YWx1ZXMpLCBdCmxpbmVzKDMrcmV2KG9yZGVyZWRfc3ViYmVkJHhfdmFsdWVzKSwgYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIob3JkZXJlZF9zdWJiZWRbLDNdKSksIGx3ZD0uMywgY29sPWFkanVzdGNvbG9yKCJsaW1lZ3JlZW4iLCAwLjMpKQp9CgoKCm9yZGVyZWRfZGVuc2l0eV90cmVuZCA8LSBkZW5zaXR5X3RyZW5kW29yZGVyKGRlbnNpdHlfdHJlbmQkeF92YWx1ZXMpLCBdCgpnYW1tZXIgPC0gbG9lc3MoYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIob3JkZXJlZF9kZW5zaXR5X3RyZW5kWywzXSkpIH4gYXMubnVtZXJpYyhvcmRlcmVkX2RlbnNpdHlfdHJlbmRbLDJdKSwgc3BhbiA9IDEpCnN1bW1hcnkoZ2FtbWVyKQpwcmVkaWN0X2dhbSA8LSBwcmVkaWN0KGdhbW1lciwgc2U9VFJVRSkKCgoKYWJsaW5lKGg9MCwgbHR5PTIsIGNvbD0iZ3JleSIpCgoKI2xpbmVzKHJldihvcmRlcmVkX2RlbnNpdHlfdHJlbmRbLDJdKSAsICBwcmVkaWN0X2dhbSRmaXQgKyBwcmVkaWN0X2dhbSRzZS5maXQsICBjb2w9Im9yYW5nZSIsIGx0eT0xLCBsd2Q9MSkKI2xpbmVzKHJldihvcmRlcmVkX2RlbnNpdHlfdHJlbmRbLDJdKSAsICBwcmVkaWN0X2dhbSRmaXQgLSBwcmVkaWN0X2dhbSRzZS5maXQsICBjb2w9Im9yYW5nZSIsIGx0eT0xLCBsd2Q9MSkKCgpsaW5lcygzK3Jldihhcy5udW1lcmljKG9yZGVyZWRfZGVuc2l0eV90cmVuZFssMl0pKSAscHJlZGljdF9nYW0kZml0LCAgY29sPSJmaXJlYnJpY2siLCBsd2Q9MSkKCgp9CgoJZnJhbWVwbG90KCkKbGluZXNfbm90X3NjYWxlZF9sb2Vzc19ub196b29tKGFsbF92ZWN0b3JzLCAiRmVydGlsZV9DcmVzYyIpCiAgIGF4aXMoMikKICAKCmBgYAoKCmBgYHtyfQojcGxvdCB0eXBlIDMKcG9seWdvbl9zY2FsZWRfbG9lc3Nfbm9fem9vbSA8LSBmdW5jdGlvbihhbGxfdmVjdG9ycywgbG9jYXRpb24pewogCiAgCiBvcmlnaW4gPC0gc3Vic2V0KGFsbF92ZWN0b3JzLCBsb2NhdGlvbl9uYW1lID09IGxvY2F0aW9uKQogCgojcGxvdChhcy5udW1lcmljKGRlbnNpdHlfdHJlbmRbLDJdKSwgYXMubnVtZXJpYyhkZW5zaXR5X3RyZW5kWyw0XSksIGNvbD1hZGp1c3Rjb2xvcigiY29ybmZsb3dlcmJsdWUiLCBhbHBoYT0wLjgpLCAgeGxhYj0ieWVhciIsIHlsYWI9IkRlbnNpdHkiLCB5bGltPWMoLTIsMiksIHhsaW09Yyg0LDIxKSwgdHlwZT0ibiIpCmcgPC0gNjc0MDQKZm9yKGcgaW4gbGV2ZWxzKGFzLmZhY3Rvcihhcy5jaGFyYWN0ZXIob3JpZ2luWyw1XSkpKSl7CnN1YmJlZCA8LSBzdWJzZXQob3JpZ2luLCBjZWxsX0lEID09IGFzLm51bWVyaWMoZykpCnN1YmJlZCA8LSBhcy5kYXRhLmZyYW1lKHN1YmJlZCkKY2xhc3Moc3ViYmVkWywyXSkgPC0gIm51bWVyaWMiCm9yZGVyZWRfc3ViYmVkIDwtIHN1YmJlZFtvcmRlcihzdWJiZWQkeF92YWx1ZXMpLCBdCmxpbmVzKDMrcmV2KG9yZGVyZWRfc3ViYmVkJHhfdmFsdWVzKSwgYXMubnVtZXJpYyhvcmRlcmVkX3N1YmJlZFssNF0pLCBsd2Q9LjMsIGNvbD1hZGp1c3Rjb2xvcigibGltZWdyZWVuIiwgMC4zKSkKfQoKCmxldnMgPC0gbGV2ZWxzKGFzLmZhY3Rvcihhcy5jaGFyYWN0ZXIob3JpZ2luWyw1XSkpKQoKCgpwcmVkaWN0aW9ucyA8LSBtYXRyaXgocmVwKE5BLCBsZW5ndGgobGV2cykqIDE4KSwgbGVuZ3RoKGxldnMpLCAxOCkKY29sbmFtZXMocHJlZGljdGlvbnMpIDwtIGFzLmNoYXJhY3Rlcig0OjIxKQoKZyA8LSAiNjc0MDQiCmkgPC0gMQpmb3IoZyBpbiBsZXZzKXsKc3ViYmVkIDwtIHN1YnNldChvcmlnaW4sIGNlbGxfSUQgPT0gYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIoZykpKQoKbW9kZWwgPC0gbG9lc3MoYXMubnVtZXJpYyhzdWJiZWRbLDRdKSB+IGFzLm51bWVyaWMoc3ViYmVkWywyXSksIHNwYW4gPSAuNSkKICAgIAogICAgcHJlZGljdGlvbnNbaSwgXSA8LSBwcmVkaWN0KG1vZGVsLCAxOjE4KQppIDwtIGkgKyAxCiAgICB9CgptIDwtIGNvbE1lYW5zKHByZWRpY3Rpb25zKQpzZCA8LSBjb2xTZHMocHJlZGljdGlvbnMpCgpnbG9iYWwubWVhbnMgPC0gbQpnbG9iYWwuU0QgPC0gc2QKCmkgPC0gd2hpY2gobmFtZXMoZ2xvYmFsLm1lYW5zKSA9PSBsb2NhdGlvbikKCnBvbHlnb24oYyggMjE6NCwgNDoyMSkgLCBjKGdsb2JhbC5tZWFucyArIGFicyhnbG9iYWwuU0QpLCByZXYoZ2xvYmFsLm1lYW5zIC0gYWJzKGdsb2JhbC5TRCkpKSwgY29sPSBhZGp1c3Rjb2xvcigiZmlyZWJyaWNrIiwgYWxwaGE9LjUpLCBib3JkZXI9TkEpCmxpbmVzKDIxOjQgLGdsb2JhbC5tZWFucywgY29sPSJmaXJlYnJpY2siLCBsd2Q9MikKCgoKYWJsaW5lKGg9MCwgbHR5PTIsIGNvbD0iZ3JleSIpCgoKI2xpbmVzKHJldihvcmRlcmVkX2RlbnNpdHlfdHJlbmRbLDJdKSAsICBwcmVkaWN0X2dhbSRmaXQgKyBwcmVkaWN0X2dhbSRzZS5maXQsICBjb2w9Im9yYW5nZSIsIGx0eT0xLCBsd2Q9MSkKI2xpbmVzKHJldihvcmRlcmVkX2RlbnNpdHlfdHJlbmRbLDJdKSAsICBwcmVkaWN0X2dhbSRmaXQgLSBwcmVkaWN0X2dhbSRzZS5maXQsICBjb2w9Im9yYW5nZSIsIGx0eT0xLCBsd2Q9MSkKCgojbGluZXMoMytyZXYoYXMubnVtZXJpYyhvcmRlcmVkX2RlbnNpdHlfdHJlbmRbLDJdKSkgLHByZWRpY3RfZ2FtJGZpdCwgIGNvbD0iZmlyZWJyaWNrIiwgbHdkPTEpCgogCiAgCn0KCmZyYW1lcGxvdCgpCnBvbHlnb25fc2NhbGVkX2xvZXNzX25vX3pvb20oYWxsX3ZlY3RvcnMsICJFX05vcnRoX0FtZXJpIikKYGBgCgoKYGBge3J9CiNwbG90IHR5cGUgNApwb2x5Z29uX3NjYWxlZF9HQU1fbm9fem9vbSA8LSBmdW5jdGlvbihhbGxfdmVjdG9ycywgbG9jYXRpb24pewogCiAgCiBvcmlnaW4gPC0gc3Vic2V0KGFsbF92ZWN0b3JzLCBsb2NhdGlvbl9uYW1lID09IGxvY2F0aW9uKQogCgojcGxvdChhcy5udW1lcmljKGRlbnNpdHlfdHJlbmRbLDJdKSwgYXMubnVtZXJpYyhkZW5zaXR5X3RyZW5kWyw0XSksIGNvbD1hZGp1c3Rjb2xvcigiY29ybmZsb3dlcmJsdWUiLCBhbHBoYT0wLjgpLCAgeGxhYj0ieWVhciIsIHlsYWI9IkRlbnNpdHkiLCB5bGltPWMoLTIsMiksIHhsaW09Yyg0LDIxKSwgdHlwZT0ibiIpCmcgPC0gNjc0MDQKZm9yKGcgaW4gbGV2ZWxzKGFzLmZhY3Rvcihhcy5jaGFyYWN0ZXIob3JpZ2luWyw1XSkpKSl7CnN1YmJlZCA8LSBzdWJzZXQob3JpZ2luLCBjZWxsX0lEID09IGFzLm51bWVyaWMoZykpCnN1YmJlZCA8LSBhcy5kYXRhLmZyYW1lKHN1YmJlZCkKY2xhc3Moc3ViYmVkWywyXSkgPC0gIm51bWVyaWMiCm9yZGVyZWRfc3ViYmVkIDwtIHN1YmJlZFtvcmRlcihzdWJiZWQkeF92YWx1ZXMpLCBdCmxpbmVzKDMrcmV2KG9yZGVyZWRfc3ViYmVkJHhfdmFsdWVzKSwgYXMubnVtZXJpYyhvcmRlcmVkX3N1YmJlZFssNF0pLCBsd2Q9LjMsIGNvbD1hZGp1c3Rjb2xvcigibGltZWdyZWVuIiwgMC4zKSkKfQoKCmxldnMgPC0gbGV2ZWxzKGFzLmZhY3Rvcihhcy5jaGFyYWN0ZXIob3JpZ2luWyw1XSkpKQoKCgpwcmVkaWN0aW9ucyA8LSBtYXRyaXgocmVwKE5BLCBsZW5ndGgobGV2cykqIDE4KSwgbGVuZ3RoKGxldnMpLCAxOCkKY29sbmFtZXMocHJlZGljdGlvbnMpIDwtIGFzLmNoYXJhY3Rlcig0OjIxKQoKZyA8LSAiNjc0MDQiCmkgPC0gMQpmb3IoZyBpbiBsZXZzKXsKc3ViYmVkIDwtIHN1YnNldChvcmlnaW4sIGNlbGxfSUQgPT0gYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIoZykpKQoKbW9kZWwgPC0gZ2FtKGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKHN1YmJlZFssNF0pKSB+IHMoYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIoc3ViYmVkWywyXSkpLCBkZiA9IDMpKSAKICAgIAogICAgcHJlZGljdGlvbnNbaSwgXSA8LSBwcmVkaWN0KG1vZGVsKQppIDwtIGkgKyAxCiAgICB9CgptIDwtIGNvbE1lYW5zKHByZWRpY3Rpb25zKQpzZCA8LSBjb2xTZHMocHJlZGljdGlvbnMpCgpnbG9iYWwubWVhbnMgPC0gbQpnbG9iYWwuU0QgPC0gc2QKCmkgPC0gd2hpY2gobmFtZXMoZ2xvYmFsLm1lYW5zKSA9PSBsb2NhdGlvbikKCnBvbHlnb24oYyggNDoyMSwgIDIxOjQpICwgYyhnbG9iYWwubWVhbnMgKyBhYnMoZ2xvYmFsLlNEKSwgcmV2KGdsb2JhbC5tZWFucyAtIGFicyhnbG9iYWwuU0QpKSksIGNvbD0gYWRqdXN0Y29sb3IoImZpcmVicmljayIsIGFscGhhPS41KSwgYm9yZGVyPU5BKQpsaW5lcyg0OjIxICxnbG9iYWwubWVhbnMsIGNvbD0iZmlyZWJyaWNrIiwgbHdkPTIpCgoKCmFibGluZShoPTAsIGx0eT0yLCBjb2w9ImdyZXkiKQoKCiNsaW5lcyhyZXYob3JkZXJlZF9kZW5zaXR5X3RyZW5kWywyXSkgLCAgcHJlZGljdF9nYW0kZml0ICsgcHJlZGljdF9nYW0kc2UuZml0LCAgY29sPSJvcmFuZ2UiLCBsdHk9MSwgbHdkPTEpCiNsaW5lcyhyZXYob3JkZXJlZF9kZW5zaXR5X3RyZW5kWywyXSkgLCAgcHJlZGljdF9nYW0kZml0IC0gcHJlZGljdF9nYW0kc2UuZml0LCAgY29sPSJvcmFuZ2UiLCBsdHk9MSwgbHdkPTEpCgoKI2xpbmVzKDMrcmV2KGFzLm51bWVyaWMob3JkZXJlZF9kZW5zaXR5X3RyZW5kWywyXSkpICxwcmVkaWN0X2dhbSRmaXQsICBjb2w9ImZpcmVicmljayIsIGx3ZD0xKQoKIAogIAp9CgpmcmFtZXBsb3QoKQpwb2x5Z29uX3NjYWxlZF9HQU1fbm9fem9vbShhbGxfdmVjdG9ycywgIkVfTm9ydGhfQW1lcmkiKQpgYGAKCgpgYGB7cn0KI3Bsb3QgdHlwZSA1CnBvbHlnb25fc2NhbGVkX2xvZXNzX3pvb20gPC0gZnVuY3Rpb24oYWxsX3ZlY3RvcnMsIGxvY2F0aW9uKXsKIAogIAogb3JpZ2luIDwtIHN1YnNldChhbGxfdmVjdG9ycywgbG9jYXRpb25fbmFtZSA9PSBsb2NhdGlvbikKIAoKI3Bsb3QoYXMubnVtZXJpYyhkZW5zaXR5X3RyZW5kWywyXSksIGFzLm51bWVyaWMoZGVuc2l0eV90cmVuZFssNF0pLCBjb2w9YWRqdXN0Y29sb3IoImNvcm5mbG93ZXJibHVlIiwgYWxwaGE9MC44KSwgIHhsYWI9InllYXIiLCB5bGFiPSJEZW5zaXR5IiwgeWxpbT1jKC0yLDIpLCB4bGltPWMoNCwyMSksIHR5cGU9Im4iKQpnIDwtIDY3NDA0CmZvcihnIGluIGxldmVscyhhcy5mYWN0b3IoYXMuY2hhcmFjdGVyKG9yaWdpblssNV0pKSkpewpzdWJiZWQgPC0gc3Vic2V0KG9yaWdpbiwgY2VsbF9JRCA9PSBhcy5udW1lcmljKGcpKQpzdWJiZWQgPC0gYXMuZGF0YS5mcmFtZShzdWJiZWQpCmNsYXNzKHN1YmJlZFssMl0pIDwtICJudW1lcmljIgpvcmRlcmVkX3N1YmJlZCA8LSBzdWJiZWRbb3JkZXIoc3ViYmVkJHhfdmFsdWVzKSwgXQpsaW5lcygzK3JldihvcmRlcmVkX3N1YmJlZCR4X3ZhbHVlcyksIGFzLm51bWVyaWMob3JkZXJlZF9zdWJiZWRbLDRdKSwgbHdkPS4zLCBjb2w9YWRqdXN0Y29sb3IoImxpbWVncmVlbiIsIDAuMykpCn0KCgpsZXZzIDwtIGxldmVscyhhcy5mYWN0b3IoYXMuY2hhcmFjdGVyKG9yaWdpblssNV0pKSkKCgoKcHJlZGljdGlvbnMgPC0gbWF0cml4KHJlcChOQSwgbGVuZ3RoKGxldnMpKiAxOCksIGxlbmd0aChsZXZzKSwgMTgpCmNvbG5hbWVzKHByZWRpY3Rpb25zKSA8LSBhcy5jaGFyYWN0ZXIoNDoyMSkKCmcgPC0gIjY3NDA0IgppIDwtIDEKZm9yKGcgaW4gbGV2cyl7CnN1YmJlZCA8LSBzdWJzZXQob3JpZ2luLCBjZWxsX0lEID09IGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKGcpKSkKCm1vZGVsIDwtIGxvZXNzKGFzLm51bWVyaWMoc3ViYmVkWyw0XSkgfiBhcy5udW1lcmljKHN1YmJlZFssMl0pLCBzcGFuID0gMSkKICAgIAogICAgcHJlZGljdGlvbnNbaSwgXSA8LSBwcmVkaWN0KG1vZGVsLCAxOjE4KQppIDwtIGkgKyAxCiAgICB9CgptIDwtIGNvbE1lYW5zKHByZWRpY3Rpb25zKQpzZCA8LSBjb2xTZHMocHJlZGljdGlvbnMpCgpnbG9iYWwubWVhbnMgPC0gbQpnbG9iYWwuU0QgPC0gc2QKCmkgPC0gd2hpY2gobmFtZXMoZ2xvYmFsLm1lYW5zKSA9PSBsb2NhdGlvbikKCnBvbHlnb24oYyggMjE6NCwgNDoyMSkgLCBjKGdsb2JhbC5tZWFucyArIGFicyhnbG9iYWwuU0QpLCByZXYoZ2xvYmFsLm1lYW5zIC0gYWJzKGdsb2JhbC5TRCkpKSwgY29sPSBhZGp1c3Rjb2xvcigiZmlyZWJyaWNrIiwgYWxwaGE9LjUpLCBib3JkZXI9TkEpCmxpbmVzKDIxOjQgLGdsb2JhbC5tZWFucywgY29sPSJmaXJlYnJpY2siLCBsd2Q9MikKCgoKYWJsaW5lKGg9MCwgbHR5PTIsIGNvbD0iZ3JleSIpCgoKI2xpbmVzKHJldihvcmRlcmVkX2RlbnNpdHlfdHJlbmRbLDJdKSAsICBwcmVkaWN0X2dhbSRmaXQgKyBwcmVkaWN0X2dhbSRzZS5maXQsICBjb2w9Im9yYW5nZSIsIGx0eT0xLCBsd2Q9MSkKI2xpbmVzKHJldihvcmRlcmVkX2RlbnNpdHlfdHJlbmRbLDJdKSAsICBwcmVkaWN0X2dhbSRmaXQgLSBwcmVkaWN0X2dhbSRzZS5maXQsICBjb2w9Im9yYW5nZSIsIGx0eT0xLCBsd2Q9MSkKCgojbGluZXMoMytyZXYoYXMubnVtZXJpYyhvcmRlcmVkX2RlbnNpdHlfdHJlbmRbLDJdKSkgLHByZWRpY3RfZ2FtJGZpdCwgIGNvbD0iZmlyZWJyaWNrIiwgbHdkPTEpCgogCiAgCn0KCm9yaWdpbiA8LSBzdWJzZXQoYWxsX3ZlY3RvcnMsIGxvY2F0aW9uX25hbWUgPT0gIkVfTm9ydGhfQW1lcmkiKQp5X21heCA8LSBtYXgob3JpZ2luWyw0XSwgbmEucm09VFJVRSkKeV9taW4gPC0gbWluKG9yaWdpblssNF0sIG5hLnJtPVRSVUUpCmZyYW1lcGxvdF9mbGV4KHlfbWluLHlfbWF4KQpheGlzKDIpCnBvbHlnb25fc2NhbGVkX2xvZXNzX3pvb20oYWxsX3ZlY3RvcnMsICJFX05vcnRoX0FtZXJpIikKCmBgYAoKCgoKYGBge3J9CmxvY2F0aW9uIDwtICJDaGluZXNlX2xvZXNzIgoKZ2FtX3Blcl9jZWxsX25vdF9zY2FsZWQgPC0gZnVuY3Rpb24oYWxsX3ZlY3RvcnMsIGxvY2F0aW9uKXsKb3JpZ2luIDwtIHN1YnNldChhbGxfdmVjdG9ycywgbG9jYXRpb25fbmFtZSA9PSBsb2NhdGlvbikKbGV2cyA8LSBsZXZlbHMoYXMuZmFjdG9yKGFzLmNoYXJhY3RlcihvcmlnaW5bLDVdKSkpCgpwcmVkaWN0aW9ucyA8LSBtYXRyaXgocmVwKE5BLCBsZW5ndGgobGV2cykqIDE4KSwgbGVuZ3RoKGxldnMpLCAxOCkKY29sbmFtZXMocHJlZGljdGlvbnMpIDwtIGFzLmNoYXJhY3Rlcig0OjIxKQoKZyA8LSAiNjI0OTMiCmkgPC0gMQpmb3IoZyBpbiBsZXZzKXsKc3ViYmVkIDwtIHN1YnNldChvcmlnaW4sIGNlbGxfSUQgPT0gYXMubnVtZXJpYyhnKSkKCm1vZGVsIDwtIGdhbShhcy5udW1lcmljKGFzLmNoYXJhY3RlcihzdWJiZWRbLDNdKSkgfiBzKGFzLm51bWVyaWMoc3ViYmVkWywyXSksIGRmID0gMykpCiAgICAKICAgIHByZWRpY3Rpb25zW2ksIF0gPC0gcHJlZGljdChtb2RlbCkKaSA8LSBpICsgMQogICAgfQoKbSA8LSBjb2xNZWFucyhwcmVkaWN0aW9ucykKc2QgPC0gY29sU2RzKHByZWRpY3Rpb25zKQpyZXR1cm4obGlzdChtLCBzZCkpCgp9CgpgYGAKCgoKYGBge3J9CiNwbG90IHR5cGUgNQpwb2x5Z29uX25vdF9zY2FsZWRfZ2FtX25vX3pvb20gPC0gZnVuY3Rpb24oYWxsX3ZlY3RvcnMsIGxvY2F0aW9uKXsKIAojZnJhbWVwbG90KCkgIAogIGZvcl8zIDwtIGdhbV9wZXJfY2VsbF9ub3Rfc2NhbGVkKGFsbF92ZWN0b3JzLCBsb2NhdGlvbikKZ2xvYmFsLm1lYW5zIDwtIGZvcl8zW1sxXV0KZ2xvYmFsLlNEIDwtIGZvcl8zW1syXV0KCiNhcy5mYWN0b3IoYWxsX3ZlY3RvcnMkbG9jYXRpb25fbmFtZSkKI2xvY2F0aW9uIDwtICJDaGluZXNlX2xvZXNzIgoKaSA8LSB3aGljaChuYW1lcyhnbG9iYWwubWVhbnMpID09IGxvY2F0aW9uKQoKcG9seWdvbihjKCA0OjIxLCAyMTo0KSAsIGMoZ2xvYmFsLm1lYW5zICsgYWJzKGdsb2JhbC5TRCksIHJldihnbG9iYWwubWVhbnMgLSBhYnMoZ2xvYmFsLlNEKSkpLCBjb2w9IGFkanVzdGNvbG9yKCJsaW1lZ3JlZW4iLCBhbHBoYT0uNSkpCmxpbmVzKDQ6MjEgLGdsb2JhbC5tZWFucywgY29sPSJsaW1lZ3JlZW4iLCBsd2Q9MikKCgogIAp9CgoJcGxvdCgyMTowLHJlcCgwLCAyMiksIHhsaW09YygyMSwwKSwgeWxpbT1jKC0wLjUsIDAuNSksIHR5cGU9Im4iLCB4YXh0PSJuIiwgeWF4dD0ibiIsIHhsYWI9IiIsIHlsYWI9IiIpCgpwb2x5Z29uX25vdF9zY2FsZWRfZ2FtX25vX3pvb20oYWxsX3ZlY3RvcnMsICJGZXJ0aWxlX0NyZXNjIikKICAgYXhpcygyKQogIAoKYGBgCgoKCmBgYHtyfQpsb2NhdGlvbiA8LSAiQ2hpbmVzZV9sb2VzcyIKCmdhbV9wZXJfY2VsbF9zY2FsZWQgPC0gZnVuY3Rpb24oYWxsX3ZlY3RvcnMsIGxvY2F0aW9uKXsKb3JpZ2luIDwtIHN1YnNldChhbGxfdmVjdG9ycywgbG9jYXRpb25fbmFtZSA9PSBsb2NhdGlvbikKbGV2cyA8LSBsZXZlbHMoYXMuZmFjdG9yKGFzLmNoYXJhY3RlcihvcmlnaW5bLDVdKSkpCgpwcmVkaWN0aW9ucyA8LSBtYXRyaXgocmVwKE5BLCBsZW5ndGgobGV2cykqIDE4KSwgbGVuZ3RoKGxldnMpLCAxOCkKY29sbmFtZXMocHJlZGljdGlvbnMpIDwtIGFzLmNoYXJhY3Rlcig0OjIxKQoKZyA8LSAiNjI0OTMiCmkgPC0gMQpmb3IoZyBpbiBsZXZzKXsKc3ViYmVkIDwtIHN1YnNldChvcmlnaW4sIGNlbGxfSUQgPT0gYXMubnVtZXJpYyhnKSkKCm1vZGVsIDwtIGdhbShhcy5udW1lcmljKHN1YmJlZFssNF0pIH4gcyhhcy5udW1lcmljKHN1YmJlZFssMl0pLCBkZiA9IDMpKQogICAgCiAgICBwcmVkaWN0aW9uc1tpLCBdIDwtIHByZWRpY3QobW9kZWwpCmkgPC0gaSArIDEKICAgIH0KCm0gPC0gY29sTWVhbnMocHJlZGljdGlvbnMpCnNkIDwtIGNvbFNkcyhwcmVkaWN0aW9ucykKcmV0dXJuKGxpc3QobSwgc2QpKQoKfQoKYGBgCgoKCgoKYGBge3J9CiNwbG90IHR5cGUgNgpwb2x5Z29uX3NjYWxlZF9nYW1fbm9fem9vbSA8LSBmdW5jdGlvbihhbGxfdmVjdG9ycywgbG9jYXRpb24pewogIAojZnJhbWVwbG90KCkgIAogIGZvcl8zIDwtIGdhbV9wZXJfY2VsbChhbGxfdmVjdG9ycywgbG9jYXRpb24pCmdsb2JhbC5tZWFucyA8LSBmb3JfM1tbMV1dCmdsb2JhbC5TRCA8LSBmb3JfM1tbMl1dCgojYXMuZmFjdG9yKGFsbF92ZWN0b3JzJGxvY2F0aW9uX25hbWUpCiNsb2NhdGlvbiA8LSAiQ2hpbmVzZV9sb2VzcyIKCmkgPC0gd2hpY2gobmFtZXMoZ2xvYmFsLm1lYW5zKSA9PSBsb2NhdGlvbikKCnBvbHlnb24oYyggNDoyMSwgMjE6NCkgLCBjKGdsb2JhbC5tZWFucyArIGFicyhnbG9iYWwuU0QpLCByZXYoZ2xvYmFsLm1lYW5zIC0gYWJzKGdsb2JhbC5TRCkpKSwgY29sPSBhZGp1c3Rjb2xvcigibGltZWdyZWVuIiwgYWxwaGE9LjUpKQpsaW5lcyg0OjIxICxnbG9iYWwubWVhbnMsIGNvbD0ibGltZWdyZWVuIiwgbHdkPTIpCgoKICAKfQoKZnJhbWVwbG90KCkKCnBvbHlnb25fc2NhbGVkX2dhbV9ub196b29tKGFsbF92ZWN0b3JzLCAiRmVydGlsZV9DcmVzYyIpCmBgYAoKCmBgYHtyfQojcGxvdCB0eXBlIDcKcG9seWdvbl9zY2FsZWRfZ2FtX3pvb20gPC0gZnVuY3Rpb24oYWxsX3ZlY3RvcnMpewogIAogIAp9CmBgYAoKCgoKCmBgYHtyfQojIyMjIyMjIyMjIyMjIyMjIyMjCgp0eXBlX251bWJlciA8LSA4Cgpjb21wbGV4X2ZpZ3VyZSA8LSBmdW5jdGlvbih0eXBlX251bWJlciwgbG9jYXRpb25fbmFtZSwgaSwgbWVhbnMsIHNkcywgcGxvdF90eXBlPTQpewoJCQojcG9seWdvbih4PWMoLTMwLC0zMCwzMCwzMCksIHk9YygtMzAsMzAsMzAsLTMwKSwgY29sPSJibGFjayIpICAKICAKYWJsaW5lKGg9MCwgbHR5PTIpCQkJCgppZihpIDwgNiApCXBvbHlnb24oeD1jKDEyLDEyLDguMiw4LjIpLCB5PWMoLTMsMywzLC0zKSwgY29sPWFkanVzdGNvbG9yKCJjb3JuZmxvd2VyYmx1ZSIsIGFscGhhPTAuMiksIGJvcmRlcj1OQSkJCQkJCQppZihpID4gNSkJcG9seWdvbih4PWMoOC4yLDguMiw0LjIsNC4yKSwgeT1jKC0zLDMsMywtMyksIGNvbD1hZGp1c3Rjb2xvcigiY29ybmZsb3dlcmJsdWUiLCBhbHBoYT0wLjIpLCBib3JkZXI9TkEpCgkJCQogIAogIAltYXRjaCA8LSBkb21lc3RpY2F0aW9uX3RpbWVzWyB3aGljaChkb21lc3RpY2F0aW9uX3RpbWVzJFJlZ2lvbiA9PSBsZXZlbHMoZG9tZXN0aWNhdGlvbl90aW1lcyRSZWdpb24pWyB0eXBlX251bWJlcl0pLCA5XQoJbWF4ZXIgPC0gbWF4KG1hdGNoLCBuYS5ybT1UUlVFKQoJYnJlYWtfb25lXzEgPC0gbWF4ZXIKCQkJYnJlYWtfdHdvXzEgPC0gbWF4ZXIgLSBxdWFudGlsZShqKVsyXQoKCQlsaW5lcyh4PWMoYnJlYWtfb25lXzEsIGJyZWFrX29uZV8xKSwgeT1jKC0zLDMpLCBjb2w9YWRqdXN0Y29sb3IoImNvcm5mbG93ZXJibHVlIiwgYWxwaGE9MC43KSwgbHdkPTMsIGxlbmQgPSAyKQkKCiAgCiAgCiAgbWF0Y2ggPC0gZG9tZXN0aWNhdGlvbl90aW1lc1sgd2hpY2goZG9tZXN0aWNhdGlvbl90aW1lcyRSZWdpb24gPT0gbGV2ZWxzKGRvbWVzdGljYXRpb25fdGltZXMkUmVnaW9uKVsgdHlwZV9udW1iZXJdKSwgMTBdCgltYXhlciA8LSBtYXgobWF0Y2gsIG5hLnJtPVRSVUUpCglicmVha19vbmVfMiA8LSBtYXhlcgoJCQlicmVha190d29fMiA8LSBtYXhlciAtIHF1YW50aWxlKGopWzJdCgkJCQkKIwlwb2x5Z29uKHg9YyhicmVha190d29fMiwgYnJlYWtfdHdvXzIsIGJyZWFrX29uZV8yLCBicmVha19vbmVfMiksIHk9YygxLCAyLCAyLCAxKSwgY29sPWFkanVzdGNvbG9yKCJsaW1lZ3JlZW4iLCBhbHBoYT0wLjUpLCBib3JkZXI9TkEpCgkJbGluZXMoeD1jKGJyZWFrX29uZV8yLCBicmVha19vbmVfMiksIHk9YygtMywzKSwgY29sPWFkanVzdGNvbG9yKCJjb3JuZmxvd2VyYmx1ZSIsIGFscGhhPTEpLCBsd2Q9MywgbGVuZCA9IDIpCQoJCgkJCiAgCQkJCQkJCgltYXRjaCA8LSBkb21lc3RpY2F0aW9uX3RpbWVzWyB3aGljaChkb21lc3RpY2F0aW9uX3RpbWVzJFJlZ2lvbiA9PSBsZXZlbHMoZG9tZXN0aWNhdGlvbl90aW1lcyRSZWdpb24pWyB0eXBlX251bWJlcl0pLCA5XQoJbWF4ZXIgPC0gbWF4KG1hdGNoLCBuYS5ybT1UUlVFKQoJaiA8LSBlY2RmKG1heGVyLW1hdGNoKQoJcHJpbnQoaikKCQoKeF9zZXEgPC0gcmV2KGMoMCxzZXEoMCwgbWF4ZXIsIGxlbmd0aC5vdXQ9MTAwKSkpCnlfc2VxIDwtIGMoMCwgaihzZXEoMCwgbWF4ZXIsIGxlbmd0aC5vdXQ9MTAwKSkpIAoKI2xpbmVzKHhfc2VxLCB5X3NlcSwgdHlwZT0ibCIsIHlsaW09YygtMSwxKSkKcG9seWdvbihjKDAsIHhfc2VxKSwgYygwLCB5X3NlcSkgLTMsIGJvcmRlcj1OQSwgY29sPWFkanVzdGNvbG9yKCJ3aGl0ZSIsIGFscGhhPTEpLCBsd2Q9LjUpCnBvbHlnb24oYygwLCB4X3NlcSksIGMoMCwgeV9zZXEpIC0zLCBib3JkZXI9ImNvcm5mbG93ZXJibHVlIiwgY29sPWFkanVzdGNvbG9yKCJjb3JuZmxvd2VyYmx1ZSIsIGFscGhhPS41KSwgbHdkPS41KQojYWJsaW5lKHY9IG1heGVyIC0gcXVhbnRpbGUoailbMl0pCgoKCW1hdGNoIDwtIGRvbWVzdGljYXRpb25fdGltZXNbIHdoaWNoKGRvbWVzdGljYXRpb25fdGltZXMkUmVnaW9uID09IGxldmVscyhkb21lc3RpY2F0aW9uX3RpbWVzJFJlZ2lvbilbIHR5cGVfbnVtYmVyXSksIDEwXQoJbWF4ZXIgPC0gbWF4KG1hdGNoLCBuYS5ybT1UUlVFKQoJaiA8LSBlY2RmKG1heGVyLW1hdGNoKQoJcHJpbnQoaikKCQoKeF9zZXEgPC0gcmV2KGMoMCxzZXEoMCwgbWF4ZXIsIGxlbmd0aC5vdXQ9MTAwKSkpCnlfc2VxIDwtIGMoMCwgaihzZXEoMCwgbWF4ZXIsIGxlbmd0aC5vdXQ9MTAwKSkpIAoKI2xpbmVzKHhfc2VxLCB5X3NlcSkKcG9seWdvbihjKDAsIHhfc2VxKSwgYygwLCB5X3NlcSkgLTMsIGJvcmRlcj0id2hpdGUiLCBjb2w9YWRqdXN0Y29sb3IoIndoaXRlIiwgYWxwaGE9MSksIGx3ZD0uNSkKcG9seWdvbihjKDAsIHhfc2VxKSwgYygwLCB5X3NlcSkgLTMsIGJvcmRlcj0id2hpdGUiLCBjb2w9YWRqdXN0Y29sb3IoImNvcm5mbG93ZXJibHVlIiwgYWxwaGE9MSksIGx3ZD0uNSkKCgoJCgkJCgoJCWlmKHBsb3RfdHlwZSA9PSAxKXsKCQpsaW5lc19zY2FsZWRfbG9lc3Nfbm9fem9vbShhbGxfdmVjdG9ycywgbG9jYXRpb25fbmFtZSkKCgl9CgkJCgkJI2FibGluZSh2PTExKQoJCgkJCmlmKHBsb3RfdHlwZSA9PSAyKXsKCQpsaW5lc19ub3Rfc2NhbGVkX2xvZXNzX25vX3pvb20oYWxsX3ZlY3RvcnMsIGxvY2F0aW9uX25hbWUpCgoJfQoKCmlmKHBsb3RfdHlwZSA9PSAzKXsKCQpwb2x5Z29uX3NjYWxlZF9sb2Vzc19ub196b29tKGFsbF92ZWN0b3JzLCBsb2NhdGlvbl9uYW1lKQoKCX0KCmlmKHBsb3RfdHlwZSA9PSA0KXsKCQpwb2x5Z29uX3NjYWxlZF9HQU1fbm9fem9vbShhbGxfdmVjdG9ycywgbG9jYXRpb25fbmFtZSkKCgl9CgoKaWYocGxvdF90eXBlID09IDUpewoJCgpwb2x5Z29uX3NjYWxlZF9sb2Vzc196b29tKGFsbF92ZWN0b3JzLCBsb2NhdGlvbl9uYW1lKQoKCgl9CgoKCgkJaWYocGxvdF90eXBlID09IDkpewoJeCA8LSBjKG1lYW5zW1tpXV0gLCBtZWFuc1tbaV1dICArIGFicyhzZHNbW2ldXSksIG1lYW5zW1tpXV0gIC0gYWJzKHNkc1tbaV1dKSkKCXNjYWxlZCA8LSBzY2FsZSh4ICwgY2VudGVyPVRSVUUpCgltZWFuc3MgPC0gc2NhbGVkWzE6MThdCglzZHNzX3BsdXMgPC0gc2NhbGVkWzE5OjM2XQoJc2Rzc19taW51cyA8LSBzY2FsZWRbMzc6NTRdCgkjYWJsaW5lKHY9MTAsIGNvbD0icmVkIikKCWxlbmd0aChzY2FsZWQpCgkjbGluZXMoNDoyMSwgbWVhbnNbW2ldXSArIHNkc1tbaV1dKQoJI3BvbHlnb24oeD1jKDQ6MjEsIDIxOjQpLCB5PWMoc2Rzc19wbHVzLCByZXYoc2Rzc19taW51cykpLCBjb2w9YWRqdXN0Y29sb3IoImZpcmVicmljayIsIGFscGhhPTEpLCBib3JkZXI9IndoaXRlIikKCXBvbHlnb24oeD1jKDIxOjQsNDoyMSksIHk9YyhzZHNzX3BsdXMsIHJldihzZHNzX21pbnVzKSksIGNvbD1hZGp1c3Rjb2xvcigiZmlyZWJyaWNrIiwgYWxwaGE9MSksIGJvcmRlcj0id2hpdGUiKQkKCX0KCQoJaWYocGxvdF90eXBlID09IDgpewojaCA8LSAzCmRlbnNpdHlfdHJlbmQgPC0gYWxsX3ZlY3RvcnNbd2hpY2goYWxsX3ZlY3RvcnNbLDFdID09IGxvY2F0aW9uX25hbWUgKSxdCgojcGxvdChhcy5udW1lcmljKGRlbnNpdHlfdHJlbmRbLDJdKSwgYXMubnVtZXJpYyhkZW5zaXR5X3RyZW5kWyw0XSksIGNvbD1hZGp1c3Rjb2xvcigiY29ybmZsb3dlcmJsdWUiLCBhbHBoYT0wLjgpLCAgeGxhYj0ieWVhciIsIHlsYWI9IkRlbnNpdHkiLCB5bGltPWMoLTIsMiksIHhsaW09Yyg0LDIxKSwgdHlwZT0ibiIpCgpmb3IoZyBpbiBsZXZlbHMoYXMuZmFjdG9yKGRlbnNpdHlfdHJlbmRbLDVdKSkpewpzdWJiZWQgPC0gc3Vic2V0KGRlbnNpdHlfdHJlbmQsIGNlbGxfSUQgPT0gZykKb3JkZXJlZF9zdWJiZWQgPC0gc3ViYmVkW29yZGVyKHN1YmJlZCR4X3ZhbHVlcyksIF0KbGluZXMoMjItYXMubnVtZXJpYyhvcmRlcmVkX3N1YmJlZFssMl0pLCBhcy5udW1lcmljKG9yZGVyZWRfc3ViYmVkWyw0XSksIGx3ZD0uMywgY29sPWFkanVzdGNvbG9yKCJsaW1lZ3JlZW4iLCAwLjMpKQp9CgoKZGVuc2l0eV90cmVuZCA8LSBhbGxfdmVjdG9yc1t3aGljaChhbGxfdmVjdG9yc1ssMV0gPT0gbG9jYXRpb25fbmFtZSksXQpvcmRlcmVkX2RlbnNpdHlfdHJlbmQgPC0gZGVuc2l0eV90cmVuZFtvcmRlcihkZW5zaXR5X3RyZW5kJHhfdmFsdWVzKSwgXQoKI2dhbW1lciA8LSBsb2Vzcyhhcy5udW1lcmljKG9yZGVyZWRfZGVuc2l0eV90cmVuZFssNF0pIH4gYXMubnVtZXJpYyhvcmRlcmVkX2RlbnNpdHlfdHJlbmRbLDJdKSwgc3BhbiA9IDEpCmdhbW1lciA8LSBnYW0oYXMubnVtZXJpYyhvcmRlcmVkX2RlbnNpdHlfdHJlbmRbLDRdKSB+IHMoYXMubnVtZXJpYyhvcmRlcmVkX2RlbnNpdHlfdHJlbmRbLDJdKSwgZGYgPSAzKSkKCnN1bW1hcnkoZ2FtbWVyKQpwcmVkaWN0X2dhbSA8LSBwcmVkaWN0KGdhbW1lciwgc2U9VFJVRSkKCgoKYWJsaW5lKGg9MCwgbHR5PTIsIGNvbD0iZ3JleSIpCgoKI2xpbmVzKHJldihvcmRlcmVkX2RlbnNpdHlfdHJlbmRbLDJdKSAsICBwcmVkaWN0X2dhbSRmaXQgKyBwcmVkaWN0X2dhbSRzZS5maXQsICBjb2w9Im9yYW5nZSIsIGx0eT0xLCBsd2Q9MSkKI2xpbmVzKHJldihvcmRlcmVkX2RlbnNpdHlfdHJlbmRbLDJdKSAsICBwcmVkaWN0X2dhbSRmaXQgLSBwcmVkaWN0X2dhbSRzZS5maXQsICBjb2w9Im9yYW5nZSIsIGx0eT0xLCBsd2Q9MSkKcG9seWdvbih4PTMrYyhyZXYob3JkZXJlZF9kZW5zaXR5X3RyZW5kWywyXSksIG9yZGVyZWRfZGVuc2l0eV90cmVuZFssMl0gKSwgeT1jKHByZWRpY3RfZ2FtJGZpdCwgcmV2KHByZWRpY3RfZ2FtJGZpdCkpLCBjb2w9YWRqdXN0Y29sb3IoImZpcmVicmljayIsIGFscGhhPS41KSxidHk9Im4iLCBsd2Q9LjIpCmxpbmVzKDMrcmV2KGFzLm51bWVyaWMob3JkZXJlZF9kZW5zaXR5X3RyZW5kWywyXSkpICxwcmVkaWN0X2dhbSRmaXQsICBjb2w9ImZpcmVicmljayIsIGx3ZD0xKQoKCgl9CgppZihwbG90X3R5cGUgPT0gMTApewoJCnBvbHlnb25fbm90X3NjYWxlZF9nYW1fbm9fem9vbShhbGxfdmVjdG9ycywgbG9jYXRpb25fbmFtZSkKCn0KCgppZihwbG90X3R5cGUgPT0gMTEpewoJCnBvbHlnb25fc2NhbGVkX2dhbV9ub196b29tKGFsbF92ZWN0b3JzLCBsb2NhdGlvbl9uYW1lKQoKCX0KCgkKCgkKfQoKCmBgYAoKYGBge3J9CmZyYW1lcGxvdCgpCmNvbXBsZXhfZmlndXJlKDEsbmFtZXMocGVyLm9yaWdpbilbMl0sIDEsIGdsb2JhbC5tZWFucywgZ2xvYmFsLlNEKSAKCmF4aXMoMSkKYXhpcygyKQpgYGAKCgojI0Fzc2VtYmxlIHRoZSBmaWd1cmUKIyMjIyBBc3NlbWJsZSB0aGUgZmlndXJlCmBgYHtyfQpxdWFydHood2lkdGg9OCwgaGVpZ2h0PTgpCgpsYXlvdXQobWF0cml4KGMoCgkxLCAxLCAxLCAxLCAxLCAxLCAxLCAxLAoJMywJNiwgNywgOCwgOSwgMTAsIDExLAk0LCAKCTMsCTUsIDUsIDUsIDUsIDUsIDUsIAk0LCAKCTMsIAkxMiwgMTMsIDE0LCAxNSwgMTYsIDE3LAk0LAoJMiwgMiwgMiwgMiwgMiwgMiwgMiwgMgoJKSwgNSwgOCwgYnlyb3c9VFJVRSksIHdpZHRoPWMoMSwgMSwgMSwgMSwgMSwgMSwgMSwgMSksIGhlaWdodD1jKDAuNywgMSwgMS41LCAxLCAwLjcpKQoKCnBhcihtYXI9YygwLDAsMCwwKSkKCiMgMS00IGxhYmVsIG1hcmdpbnMKYmxhbmtwbG90IDwtIGZ1bmN0aW9uKCl7CgkKCXBsb3QoMCwwLCB4bGltPWMoNCwyMSksIHlsaW09YygxLCAxLjI1KSwgYnR5PSJuIiwgdHlwZT0ibiIsIHhheHQ9Im4iLCB5YXh0PSJuIiwgeGxhYj0iIiwgeWxhYj0iIikKfQoKYmxhbmtwbG90KCkKYmxhbmtwbG90KCkKYmxhbmtwbG90KCkKYmxhbmtwbG90KCkKCgoKCgoKCmQgPC0gcmVhZFBORygiNDA5NjIucG5nIikKZGltKGQpCnBhcihtYXI9YygwLDAsMCwwKSkKcGxvdCgwOjM2MCwwOjM2MCx0eXBlPSJuIix4bGltPWMoMjAsMzYwKSx5bGltPWMoNjUsMjk1KSwgeWF4dD0ibiIsIHhheHQ9Im4iKQpyYXN0ZXJJbWFnZShkLCAtMjguNSwgLTEzLjUsIDM4OCwgMzc1LCBpbnRlcnBvbGF0ZT1UUlVFLCBjb2w9ZCkKYXhpcygyLCBsYWJlbD1zZXEoLTkwLCA5MCwgbGVuZ3RoLm91dCA9IDE5KSwgYXQ9c2VxKDEsIDM2MCwgbGVuZ3RoLm91dCA9IDE5KSwgbGFzPTEpCm10ZXh0KCJsYXRpdHVkZSIsIDIsIGxpbmU9NCwgYXQ9MTgwKQphYmxpbmUoaD1zZXEoMSwgMzYwLCBsZW5ndGgub3V0ID0gMTkpLCBjb2w9YWRqdXN0Y29sb3IoImdyZXkxMCIsIGFscGhhPSAwLjQpLCBsd2Q9MSkKYWJsaW5lKGg9MTgwLCBjb2w9YWRqdXN0Y29sb3IoIndoaXRlIiwgYWxwaGE9IC41KSwgbHdkPTEpCgoKCgpmbGV4ZnJhbWUgPC0gRkFMU0UKCmZvcihpIGluIDE6MTIpewoKCQogIGZsZXhfMSA8LSBmdW5jdGlvbihhbGxfdmVjdG9ycywgbG9jYXRpb25zKXsKICAgIG9yaWdpbiA8LSBzdWJzZXQoYWxsX3ZlY3RvcnMsIGxvY2F0aW9uX25hbWUgPT0gbG9jYXRpb25zKQp5X21heCA8LSBtYXgob3JpZ2luWyw0XSwgbmEucm09VFJVRSkKeV9taW4gPC0gbWluKG9yaWdpblssNF0sIG5hLnJtPVRSVUUpCmZyYW1lcGxvdF9mbGV4KHlfbWluLHlfbWF4KQoKICB9CiAgCiAgZmxleF8yIDwtIGZ1bmN0aW9uKGFsbF92ZWN0b3JzLCBsb2NhdGlvbnMpewogICAgb3JpZ2luIDwtIHN1YnNldChhbGxfdmVjdG9ycywgbG9jYXRpb25fbmFtZSA9PSBsb2NhdGlvbnMpCnlfbWF4IDwtIG1heChvcmlnaW5bLDRdLCBuYS5ybT1UUlVFKQp5X21pbiA8LSBtaW4ob3JpZ2luWyw0XSwgbmEucm09VFJVRSkKZnJhbWVwbG90X2ZsZXgoeV9taW4seV9tYXgpCgogICAgCiAgICAKICB9CgogIAoJaWYoZmxleGZyYW1lID09IEZBTFNFKXtpZihpID4gNil7ZnJhbWVwbG90KCl9ZWxzZXtmcmFtZXBsb3RfYm90dG9tKCl9fQoKCQoJCSMjIGN1c3RvbWl6ZSBwb2x5Z29ucyBmb3IgZWFjaCBncmFwaAoJaWYoaSA9PSAxKXsgI21lc29hbWVyaWNhICAjdmFsdWVzIGZyb20gTGFyc29uCgkJCgkgIGlmKGZsZXhmcmFtZSA9PSBUUlVFKXtmbGV4XzEoYWxsX3ZlY3RvcnMsICJNZXNvYW1lcmljYSIpfQoJCQljb21wbGV4X2ZpZ3VyZSgzLCAiTWVzb2FtZXJpY2EiLCBpLCBtZWFucywgc2RzKQoJCQkJCgkKCQl9CgkKCQoJIyMjIyMjIyMjCglpZihpID09IDIgKXsgI05XIGxvd2xhbmRzIFNBICAjdmFsdWVzIGZyb20gTGFyc29uCgkJaWYoZmxleGZyYW1lID09IFRSVUUpe2ZsZXhfMShhbGxfdmVjdG9ycywgIk5XX0xvd2xhbmRfU0EiKX0KCQljb21wbGV4X2ZpZ3VyZSg2LCAiTldfTG93bGFuZF9TQSIsIGksIG1lYW5zLCBzZHMpCgkKCgkJfQoJCQoJCSMjIyMjIyMjIwoJaWYoIGkgPT0gMyl7ICNOVyBsb3dsYW5kcyBTQSAgI3ZhbHVlcyBmcm9tIExhcnNvbgoJCWlmKGZsZXhmcmFtZSA9PSBUUlVFKXtmbGV4XzEoYWxsX3ZlY3RvcnMsICJOX0xvd2xhbmRfU0EiKX0KCQljb21wbGV4X2ZpZ3VyZSg2LCAiTl9Mb3dsYW5kX1NBIiwgaSwgbWVhbnMsIHNkcykKCQkKCQl9CgoKCQkjIyMjIyMjIyMKCWlmKGkgPT0gNCl7ICNGZXJ0aWxlIGNyZXNjZW50IGFrYSBTb3V0aHdlc3QgYXNpYSAgI3ZhbHVlcyBmcm9tIExhcnNvbgoJCQoJCWlmKGZsZXhmcmFtZSA9PSBUUlVFKXtmbGV4XzEoYWxsX3ZlY3RvcnMsICJGZXJ0aWxlX0NyZXNjIil9Cgljb21wbGV4X2ZpZ3VyZSg4LCAiRmVydGlsZV9DcmVzYyIsIGksIG1lYW5zLCBzZHMpCgkJCQkKCQl9CgkJCgkJIyMjIyMjIyMjCglpZihpID09IDUpeyAjbG9lc3MgcGxhdGVhdSAgI3ZhbHVlcyBmcm9tIExhcnNvbgoJCWlmKGZsZXhmcmFtZSA9PSBUUlVFKXtmbGV4XzEoYWxsX3ZlY3RvcnMsICJDaGluZXNlX2xvZXNzIil9CgkJY29tcGxleF9maWd1cmUoMiwgIkNoaW5lc2VfbG9lc3MiLCBpLCBtZWFucywgc2RzKQoJCQkKCQl9CgkJCgkJCgkJIyMjIyMjIyMjCglpZihpID09IDYpeyAjbmV3IGd1aW5lYSAgI3ZhbHVlcyBmcm9tIExhcnNvbgoJCWlmKGZsZXhmcmFtZSA9PSBUUlVFKXtmbGV4XzEoYWxsX3ZlY3RvcnMsICJOZXdfR3VpbmVhIiApfQoJCWNvbXBsZXhfZmlndXJlKDQsICJOZXdfR3VpbmVhIiwgaSwgbWVhbnMsIHNkcykKCQkKCQl9CgoKIyMjIyMjIyMjCglpZihpID09IDcpeyAjRWFzdGVybiBOLkEuICAjdmFsdWVzIGZyb20gTGFyc29uCgkJaWYoZmxleGZyYW1lID09IFRSVUUpe2ZsZXhfMihhbGxfdmVjdG9ycywgIkVfTm9ydGhfQW1lcmkiKX0KCQljb21wbGV4X2ZpZ3VyZSg1LCAiRV9Ob3J0aF9BbWVyaSIsIGksIG1lYW5zLCBzZHMpCgkJCgkJCX0KCgoJCSMjIyMjIyMjIwoJaWYoaSA9PSA4KXsgI0FuZGVzICAjdmFsdWVzIGZyb20gTGFyc29uCgkJaWYoZmxleGZyYW1lID09IFRSVUUpe2ZsZXhfMihhbGxfdmVjdG9ycywgIkMvU19BbmRlcyIpfQoJCWNvbXBsZXhfZmlndXJlKDYsICJDL1NfQW5kZXMiLCBpLCBtZWFucywgc2RzKQoJCQoJCQkJfQoKCiMjIyMjIyMjIwoJaWYoaSA9PSA5KXsgI1cuIEFmcmljYW4gU2F2ICAjdmFsdWVzIGZyb20gTGFyc29uCgkJaWYoZmxleGZyYW1lID09IFRSVUUpe2ZsZXhfMihhbGxfdmVjdG9ycywgIldfQWZyaWNhbl9TYXYiKX0KCQljb21wbGV4X2ZpZ3VyZSgxLCAiV19BZnJpY2FuX1NhdiIsIGksIG1lYW5zLCBzZHMpCgkJCgkJCX0KCgojIyMjIyMjIyMKCWlmKGkgPT0gMTApeyAjU3VkYW5pYyBzYXYgICN2YWx1ZXMgZnJvbSBMYXJzb24KCQlpZihmbGV4ZnJhbWUgPT0gVFJVRSl7ZmxleF8yKGFsbF92ZWN0b3JzLCAiU3VkYW5pY19TYXZhbiIpfQoJCWNvbXBsZXhfZmlndXJlKDEsICJTdWRhbmljX1NhdmFuIiwgaSwgbWVhbnMsIHNkcykKCQkKCQkJCX0KCgojIyMjIyMjIyMKCWlmKGkgPT0gMTEpeyAjR2FuZ2VzICAjdmFsdWVzIGZyb20gTGFyc29uCgkJCgkJaWYoZmxleGZyYW1lID09IFRSVUUpe2ZsZXhfMihhbGxfdmVjdG9ycywgIkdhbmdlc19FX0luZGkiKX0KCQljb21wbGV4X2ZpZ3VyZSg3LCAiR2FuZ2VzX0VfSW5kaSIsIGksIG1lYW5zLCBzZHMpIAoJCQoJCX0KCgojIyMjIyMjIyMKCWlmKGkgPT0gMTIpeyAjIkxvd2VyLU1pZGRsZVkiICAjdmFsdWVzIGZyb20gTGFyc29uCgkJaWYoZmxleGZyYW1lID09IFRSVUUpe2ZsZXhfMihhbGxfdmVjdG9ycywgIkxvd2VyLU1pZGRsZVkiKX0KCQljb21wbGV4X2ZpZ3VyZSgyLCAiTG93ZXItTWlkZGxlWSIsIGksIG1lYW5zLCBzZHMpCgkJIAoJCSAJCX0KCgkJCgkJCgkJI2xpbmVzKDQ6MjEsIG1lYW5zW1tpXV0pCgkJCgkJI2FibGluZShoID0gMSwgY29sPWFkanVzdGNvbG9yKCJmb3Jlc3RncmVlbiIsIGFscGhhPS41KSwgbHR5PTIpCgkJCgkjIGFkZCBheGVzIHRvIHNvbWUgbG9jYXRpb25zCglpZihpID09IDEgfCBpID09IDcpe2F4aXMoMiwgYXQ9c2VxKC0yLDIsIGJ5PTAuNSksIGxhYmVsPXNlcSgtMiwyLCBieT0wLjUpLCBsYXM9MSl9CglpZihpID09IDYgfCBpID09IDEyKXtheGlzKDQsIGF0PXNlcSgtMiwyLCBieT0wLjUpLCBsYWJlbD1zZXEoLTIsMiwgYnk9MC41KSwgbGFzPTEpfQoJI2lmKGkgPT0gNiB8IGkgPT0gMTIpe2F4aXMoNCwgYXQ9c2VxKDIsMywgYnk9MC4yNSksIGxhYmVsPXNlcSgwLDEsIGJ5PTAuMjUpLCBsYXM9MSkKCSMJYXhpcyg0LCBhdD1zZXEoLTEsMCwgYnk9MC4yNSksIGxhYmVsPXJldihzZXEoMCwxLCBieT0wLjI1KSksIGxhcz0xKQoJIwl9CglpZihpID4gNil7YXhpcygxKX0gZWxzZXtheGlzKDMpfQoKCQoJIyBhZGQgdGV4dCAKCWlmKGkgPCA3KXtwb2x5Z29uKHg9YygtMzAsIC0zMCwgMzAsIDMwKSwgeT1jKC0zLjEsIC0zLjUsIC0zLjUsIC0zLjEpLCBjb2w9ImJsYWNrIikKCW10ZXh0KG5hbWVfdmVjdG9yW2ldLCAxLCBsaW5lPS0wLjksIGNvbD0id2hpdGUiLCBjZXg9MC41KX0KCQoJaWYoaSA+IDYpe3BvbHlnb24oeD1jKC0zMCwgLTMwLCAzMCwgMzApLCB5PWMoMi4xLCAyLjUsIDIuNSwgMi4xKSwgY29sPSJibGFjayIpCgltdGV4dChuYW1lX3ZlY3RvcltpXSwgMywgbGluZT0tMC44LCBjb2w9IndoaXRlIiwgY2V4PTAuNSl9CgkKCSMgYWRkIGF4aXMgbGFiZWxzCglpZihpID09IDEgfCBpID09ICA3KXttdGV4dCgic2NhbGVkIGRlbnNpdHkgcG90ZW50aWFsIiwgMiwgbGluZT00LCBhdD0xKX0KCWlmKGkgPT0gIDMpe210ZXh0KCJUaG91c2FuZCB5ZWFycyBiZWZvcmUgcHJlc2VudCIsIDMsIGxpbmU9My41LCBhdCA9NSl9CglpZihpID09ICA5KXttdGV4dCgiVGhvdXNhbmQgeWVhcnMgYmVmb3JlIHByZXNlbnQiLCAxLCBsaW5lPTMuNSwgYXQgPTUpCgkJCgkJfQoJCn0KCgoKCgoKc2F2ZVRvUERGIDwtIGZ1bmN0aW9uKC4uLikgewogICAgZCA9IGRldi5jb3B5KHBkZiwuLi4pCiAgICBkZXYub2ZmKGQpCn0KCnNhdmVUb1BORyA8LSBmdW5jdGlvbiguLi4pIHsKICAgIGQgPSBkZXYuY29weShwbmcsLi4uKQogICAgZGV2Lm9mZihkKQp9CgojIyBUcnkgdGhlbSBvdXQKCnNhdmVUb1BERigibXkucGRmIiwgaGVpZ2h0PTgsd2lkdGg9OCkKc2F2ZVRvUE5HKCJteS5wbmciLCBoZWlnaHQ9OCwgd2lkdGg9OCwgdW5pdHM9ImluIiwgcmVzPTMwMCkKZGV2Lm9mZigpCgoKCmBgYAoK